diff --git a/src/controls/ApplicationWindow.qml b/src/controls/ApplicationWindow.qml index 41cd87a..4f6dbdb 100644 --- a/src/controls/ApplicationWindow.qml +++ b/src/controls/ApplicationWindow.qml @@ -1,456 +1,564 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 import QtQuick.Window 2.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import QtQuick.Controls.Material 2.1 import "private" Kirigami.AbstractApplicationWindow { id: root visible: true width: Screen.width * (Kirigami.Settings.isMobile ? 1 : 0.4) height: Screen.height * (Kirigami.Settings.isMobile ? 1 : 0.4) contentItem.anchors.leftMargin: root.sideBar && !root.globalDrawer ? ((root.sideBar.collapsible && root.sideBar.collapsed) ? root.sideBar.collapsedSize : (root.sideBar.modal ? 0 : root.sideBar.width * root.sideBar.position)) : (!root.sideBar && root.globalDrawer && (root.globalDrawer.modal === false) ? root.globalDrawer.width * root.globalDrawer.position : 0) property Maui.AbstractSideBar sideBar /***************************************************/ /******************** ALIASES *********************/ /*************************************************/ property alias headBar : _headBar property alias footBar: _footBar property alias dialog: dialogLoader.item property alias leftIcon : menuBtn property alias menuButton : menuBtn property alias mainMenu : mainMenu.contentData property alias about : aboutDialog property alias accounts: _accountsDialogLoader.item property var currentAccount: Maui.App.accounts.currentAccount property alias notifyDialog: _notify wideScreen: isWide /***************************************************/ /*********************** UI ***********************/ /*************************************************/ property bool isWide : root.width >= Kirigami.Units.gridUnit * 30 - property string colorSchemeName : Qt.application.name - + + property Flickable flickable : null + property int footerPositioning : Kirigami.Settings.isMobile && flickable ? ListView.PullBackHeader : ListView.InlineFooter + property int headerPositioning : Kirigami.Settings.isMobile && flickable ? ListView.PullBackHeader : ListView.InlineHeader + /***************************************************/ /********************* COLORS *********************/ /*************************************************/ property color headBarBGColor: Kirigami.Theme.backgroundColor property color headBarFGColor: Kirigami.Theme.textColor /***************************************************/ /**************** READONLY PROPS ******************/ /*************************************************/ readonly property bool isMobile : Kirigami.Settings.isMobile readonly property bool isAndroid: Maui.Handy.isAndroid readonly property bool isTouch: Maui.Handy.isTouch readonly property real screenWidth : Screen.width readonly property real screenHeight : Screen.height /***************************************************/ /******************** SIGNALS *********************/ /*************************************************/ signal menuButtonClicked(); onClosing: { if(!Kirigami.Settings.isMobile) { const height = root.height const width = root.width const x = root.x const y = root.y Maui.FM.saveSettings("GEOMETRY", Qt.rect(x, y, width, height), "WINDOW") } } property bool isPortrait: Screen.primaryOrientation === Qt.PortraitOrientation || Screen.primaryOrientation === Qt.InvertedPortraitOrientation onIsPortraitChanged: { if(isPortrait) { console.log("PORTARIT MODE CHANGED") width: Screen.width height: Screen.height } } // onHeadBarBGColorChanged: // { // if(!isMobile && colorSchemeName.length > 0) // Maui.KDE.setColorScheme(colorSchemeName, headBarBGColor, headBarFGColor) // else if(isAndroid && headBar.position === ToolBar.Header) // Maui.Android.statusbarColor(headBarBGColor, false) // else if(isAndroid && headBar.position === ToolBar.Footer) // Maui.Android.statusbarColor(Kirigami.Theme.viewBackgroundColor, true) // // } // // onHeadBarFGColorChanged: // { // if(!isAndroid && !isMobile && colorSchemeName.length > 0 && headBar.position === ToolBar.Header) // Maui.KDE.setColorScheme(colorSchemeName, headBarBGColor, headBarFGColor) // else if(isAndroid && headBar.position === ToolBar.Header) // Maui.Android.statusbarColor(headBarBGColor, false) // else if(isAndroid && headBar.position === ToolBar.Footer) // Maui.Android.statusbarColor(Kirigami.Theme.viewBackgroundColor, true) // } /* * background: Rectangle * { * color: bgColor } */ Component { id: _accountsComponent ColumnLayout { visible: Maui.App.handleAccounts spacing: Maui.Style.space.medium Kirigami.Icon { visible: Maui.App.accounts.currentAccountIndex >= 0 source: "user-identity" Layout.preferredHeight: Maui.Style.iconSizes.large Layout.preferredWidth: Maui.Style.iconSizes.large Layout.alignment: Qt.AlignCenter Layout.margins: Maui.Style.space.medium } Label { visible: Maui.App.accounts.currentAccountIndex >= 0 text: currentAccount.user Layout.fillWidth: true horizontalAlignment: Qt.AlignHCenter elide: Text.ElideMiddle wrapMode: Text.NoWrap font.bold: true font.weight: Font.Bold } Kirigami.Separator { visible: _accountsListing.count > 0 Layout.fillWidth: true } ListBrowser { id: _accountsListing visible: _accountsListing.count > 0 Layout.fillWidth: true Layout.preferredHeight: Math.min(listView.contentHeight, 300) listView.spacing: Maui.Style.space.medium Kirigami.Theme.backgroundColor: "transparent" currentIndex: Maui.App.accounts.currentAccountIndex model: Maui.BaseModel { list: Maui.App.accounts } delegate: Maui.ListBrowserDelegate { iconSource: "amarok_artist" iconSizeHint: Maui.Style.iconSizes.medium label1.text: model.user label2.text: model.server width: _accountsListing.width height: Maui.Style.rowHeight * 1.2 leftPadding: Maui.Style.space.tiny rightPadding: Maui.Style.space.tiny onClicked: Maui.App.accounts.currentAccountIndex = index } Component.onCompleted: { if(_accountsListing.count > 0) Maui.App.accounts.currentAccountIndex = 0 } } Kirigami.Separator { visible: _accountsListing.count > 0 Layout.fillWidth: true } Button { Layout.margins: Maui.Style.space.small Layout.preferredHeight: implicitHeight Layout.alignment: Qt.AlignCenter text: qsTr("Manage accounts") icon.name: "list-add-user" onClicked: { if(root.accounts) accounts.open() mainMenu.close() } Kirigami.Theme.backgroundColor: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.1) Kirigami.Theme.textColor: Kirigami.Theme.textColor } Kirigami.Separator { Layout.fillWidth: true } } } + + Connections + { + target: root.flickable ? root.flickable : null + enabled: root.flickable && (root.header || root.footer) + property int oldContentY + property bool updatingContentY: false + + onContentYChanged: + { + if(root.flickable.atYBeginning && !root.flickable.dragging) + { + root.header.y = 0 + root.footer.height = root.footer.implicitHeight + oldContentY = 0 + updatingContentY = false + return; + } + + if (updatingContentY || !root.flickable) + { + oldContentY = root.flickable.contentY; + return; + //TODO: merge + //if moves but not dragging, just update oldContentY + } else if (!root.flickable.dragging) + { + oldContentY = root.flickable.contentY; + return; + } + + + if (root.footerPositioning === ListView.InlineFooter && root.footer) + { + root.footer.height = root.footer.implicitHeight + + } else if (root.footerPositioning === ListView.PullBackFooter && root.footer) + { + var oldFHeight = root.footer.height; + + root.footer.height = Math.max(0, + Math.min(root.footer.implicitHeight, + root.footer.height + oldContentY - root.flickable.contentY)); + + //if the implicitHeight is changed, use that to simulate scroll + if (oldFHeight !== root.footer.height) { + updatingContentY = true; + updatingContentY = false; + } else { + oldContentY = root.flickable.contentY; + } + } + + if (root.headerPositioning === ListView.InlineHeader && root.header) + { + root.header.height = root.header.implicitHeight + + } else if (root.headerPositioning === ListView.PullBackHeader && root.header) + { + var oldHHeight = root.header.height; + + root.header.height = Math.max(0, + Math.min(root.header.implicitHeight, + root.header.height + oldContentY - root.flickable.contentY)); + + //if the implicitHeight is changed, use that to simulate scroll + if (oldHHeight !== root.header.height) { + updatingContentY = true + root.flickable.contentY -= (oldHHeight - root.header.height) + updatingContentY = false; + } else { + oldContentY = root.flickable.contentY; + } + } + } + + onMovementEnded: + { + if (root.headerPositioning === ListView.PullBackHeader && root.header) + { + if (root.header.height > (root.header.implicitHeight/2) ) + { + root.header.height = root.header.implicitHeight + + } else + { + root.header.height = 0 + } + } + + if (root.footerPositioning === ListView.PullBackFooter && root.footer) + { + if (root.footer.height > (root.footer.implicitHeight/2) ) + { + root.footer.height = root.footer.implicitHeight + + } else + { + root.footer.height = 0 + } + } + + } + } property Maui.ToolBar mheadBar : Maui.ToolBar { id: _headBar visible: count > 1 position: ToolBar.Header width: root.width height: implicitHeight + // Kirigami.Theme.backgroundColor: headBarBGColor // Kirigami.Theme.textColor: headBarFGColor // Kirigami.Theme.inherit: true leftContent: [ ToolButton { id: menuBtn icon.name: "application-menu" icon.color: headBarFGColor icon.width: Maui.Style.iconSizes.medium icon.height: Maui.Style.iconSizes.medium checked: mainMenu.visible onClicked: { menuButtonClicked() mainMenu.visible ? mainMenu.close() : mainMenu.popup(parent, parent.x , parent.height+ Maui.Style.space.medium) } Menu { id: mainMenu modal: true z: 999 width: Maui.Style.unit * 250 Loader { id: _accountsMenuLoader width: parent.width * 0.9 anchors.horizontalCenter: parent.horizontalCenter active: Maui.App.handleAccounts sourceComponent: Maui.App.handleAccounts ? _accountsComponent : null } MenuItem { text: qsTr("About") icon.name: "documentinfo" onTriggered: aboutDialog.open() } } } ] } property Maui.ToolBar mfootBar : Maui.ToolBar { id: _footBar visible: count position: ToolBar.Footer width: root.width height: implicitHeight } header: headBar.count && headBar.position === ToolBar.Header ? headBar : null footer: Column { id: _footer visible : children children: { if(headBar.position === ToolBar.Footer && headBar.count && footBar.count) return [footBar , headBar] else if(headBar.position === ToolBar.Footer && headBar.count) return [headBar] else if(footBar.count) return [footBar] else return [] } } Maui.AboutDialog { id: aboutDialog } Loader { id: _accountsDialogLoader source: Maui.App.handleAccounts ? "private/AccountsHelper.qml" : "" } Maui.Dialog { id: _notify property var cb : ({}) property alias iconName : _notifyTemplate.iconSource property alias title : _notifyTemplate.label1 property alias body: _notifyTemplate.label2 verticalAlignment: Qt.AlignTop defaultButtons: _notify.cb !== null rejectButton.visible: false onAccepted: { if(_notify.cb) { _notify.cb() _notify.close() } } page.padding: Maui.Style.space.medium footBar.background: null maxHeight: Math.max(Maui.Style.iconSizes.large + Maui.Style.space.huge, (_notifyTemplate.implicitHeight)) + Maui.Style.space.big + footBar.height maxWidth: Kirigami.Settings.isMobile ? parent.width * 0.9 : Maui.Style.unit * 500 widthHint: 0.8 Timer { id: _notifyTimer onTriggered: { if(_mouseArea.containsPress || _mouseArea.containsMouse) return; _notify.close() } } onClosed: _notifyTimer.stop() Maui.ListItemTemplate { id: _notifyTemplate anchors.fill: parent iconSizeHint: Maui.Style.iconSizes.huge label1.font.bold: true label1.font.weight: Font.Bold label1.font.pointSize: Maui.Style.fontSizes.big iconSource: "dialog-warning" } MouseArea { id: _mouseArea height: parent.height width: parent.width anchors.centerIn: parent hoverEnabled: true } function show(callback) { _notify.cb = callback || null _notifyTimer.start() _notify.open() } } Loader { id: dialogLoader } Component.onCompleted: { if(isAndroid) { if(headBar.position === ToolBar.Footer) Maui.Android.statusbarColor(Kirigami.Theme.backgroundColor, true) else Maui.Android.statusbarColor(headBar.Kirigami.Theme.backgroundColor, true) } if(!Kirigami.Settings.isMobile) { const rect = Maui.FM.loadSettings("GEOMETRY", "WINDOW", Qt.rect(root.x, root.y, root.width, root.height)) root.x = rect.x root.y = rect.y root.width = rect.width root.height = rect.height } } Connections { target: Maui.App onSendNotification: notify(icon, title, body, callback, timeout, buttonText) } function notify(icon, title, body, callback, timeout, buttonText) { _notify.iconName = icon || "emblem-warning" _notify.title.text = title _notify.body.text = body _notifyTimer.interval = timeout ? timeout : 2500 _notify.acceptButton.text = buttonText || qsTr ("Accept") _notify.show(callback) } } diff --git a/src/controls/FileBrowser.qml b/src/controls/FileBrowser.qml index e73b8ae..6a60749 100644 --- a/src/controls/FileBrowser.qml +++ b/src/controls/FileBrowser.qml @@ -1,1192 +1,1199 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.10 import QtQuick.Controls 2.10 import QtQuick.Layouts 1.3 import QtQml.Models 2.3 import QtQml 2.1 import org.kde.kirigami 2.8 as Kirigami import org.kde.mauikit 1.0 as Maui import org.kde.mauikit 1.1 as MauiLab import "private" Maui.Page { id: control property url currentPath onCurrentPathChanged: { if(control.browserView) control.browserView.path = control.currentPath } property int viewType : Maui.FMList.LIST_VIEW onViewTypeChanged: browserView.viewType = control.viewType property int thumbnailsSize : Maui.Style.iconSizes.large * 1.7 property var indexHistory : [] property bool isCopy : false property bool isCut : false property bool group : false //group properties from the browser since the browser views are loaded async and //their properties can not be accesed inmediately, so they are stored here and then when completed they are set - property BrowserSettings settings : BrowserSettings {} + property alias settings : _settings + BrowserSettings {id: _settings } property alias selectionBar : selectionBarLoader.item property alias browserView : _browserList.currentItem readonly property Maui.FMList currentFMList : browserView.currentFMList readonly property Maui.BaseModel currentFMModel : browserView.currentFMModel property alias previewer : previewer property alias menu : browserMenu.contentData property alias itemMenu: itemMenu property alias dialog : dialogLoader.item signal itemClicked(int index) signal itemDoubleClicked(int index) signal itemRightClicked(int index) signal itemLeftEmblemClicked(int index) signal itemRightEmblemClicked(int index) signal rightClicked() signal newBookmark(var paths) Kirigami.Theme.colorSet: Kirigami.Theme.View Kirigami.Theme.inherit: false onGoBackTriggered: control.goBack() onGoForwardTriggered: control.goNext() focus: true - footBar.visible: false || String(control.currentPath).startsWith("trash:/") + flickable: browserView.currentView.flickable + + footBar.visible: Maui.FM.loadSettings("StatusBar", "SETTINGS", false) == "true" || String(control.currentPath).startsWith("trash:/") footBar.middleContent: Maui.TextField { Layout.fillWidth: true visible: control.currentFMList.count > 0 placeholderText: qsTr("Filter") + " " + control.currentFMList.count + " " + qsTr("files") onAccepted: control.browserView.filter = text onCleared: control.browserView.filter = "" } footBar.rightContent: [ ToolButton { icon.name: "zoom-in" onClicked: zoomIn() }, ToolButton { icon.name: "zoom-out" onClicked: zoomOut() }, ToolButton { visible: String(control.currentPath).startsWith("trash:/") icon.name: "trash-empty" text: qsTr("Empty trash") onClicked: Maui.FM.emptyTrash() } ] headBar.position: Kirigami.Settings.isMobile ? ToolBar.Footer : ToolBar.Header headBar.rightContent:[ ToolButton { icon.name: "item-select" checkable: true checked: settings.selectionMode onClicked: settings.selectionMode = !settings.selectionMode }, Maui.ToolButtonMenu { icon.name: "view-sort" MenuItem { text: qsTr("Folders first") checked: control.currentFMList.foldersFirst checkable: true onTriggered: control.currentFMList.foldersFirst = !control.currentFMList.foldersFirst } MenuSeparator {} MenuItem { text: qsTr("Type") checked: control.currentFMList.sortBy === Maui.FMList.MIME checkable: true onTriggered: control.currentFMList.sortBy = Maui.FMList.MIME autoExclusive: true } MenuItem { text: qsTr("Date") checked: control.currentFMList.sortBy === Maui.FMList.DATE checkable: true onTriggered: control.currentFMList.sortBy = Maui.FMList.DATE autoExclusive: true } MenuItem { text: qsTr("Modified") checkable: true checked: control.currentFMList.sortBy === Maui.FMList.MODIFIED onTriggered: control.currentFMList.sortBy = Maui.FMList.MODIFIED autoExclusive: true } MenuItem { text: qsTr("Size") checkable: true checked: control.currentFMList.sortBy === Maui.FMList.SIZE onTriggered: control.currentFMList.sortBy = Maui.FMList.SIZE autoExclusive: true } MenuItem { text: qsTr("Name") checkable: true checked: control.currentFMList.sortBy === Maui.FMList.LABEL onTriggered: control.currentFMList.sortBy = Maui.FMList.LABEL autoExclusive: true } MenuSeparator{} MenuItem { id: groupAction text: qsTr("Group") checkable: true checked: control.group onTriggered: { control.group = !control.group if(control.group) control.groupBy() else browserView.currentView.section.property = "" } } }, ToolButton { id: _optionsButton icon.name: "overflow-menu" enabled: currentFMList.pathType !== Maui.FMList.TAGS_PATH && currentFMList.pathType !== Maui.FMList.TRASH_PATH && currentFMList.pathType !== Maui.FMList.APPS_PATH onClicked: { if(browserMenu.visible) browserMenu.close() else browserMenu.show(_optionsButton, 0, height) } checked: browserMenu.visible checkable: false } ] headBar.leftContent: [ ToolButton { icon.name: "go-previous" onClicked: control.goBack() }, ToolButton { icon.name: "go-next" onClicked: control.goNext() }, Maui.ToolActions { direction: Qt.Vertical currentAction: switch(browserView.viewType) { case Maui.FMList.ICON_VIEW: return actions[0] case Maui.FMList.LIST_VIEW: return actions[1] case Maui.FMList.MILLERS_VIEW: return actions[2] } Action { icon.name: "view-list-icons" text: qsTr("Grid") onTriggered: control.viewType = Maui.FMList.ICON_VIEW checked: browserView.viewType === Maui.FMList.ICON_VIEW icon.width: Maui.Style.iconSizes.medium } Action { icon.name: "view-list-details" text: qsTr("List") onTriggered: control.viewType = Maui.FMList.LIST_VIEW icon.width: Maui.Style.iconSizes.medium checkable: true checked: browserView.viewType === Maui.FMList.LIST_VIEW } Action { icon.name: "view-file-columns" text: qsTr("Columns") onTriggered: control.viewType = Maui.FMList.MILLERS_VIEW icon.width: Maui.Style.iconSizes.medium checkable: true checked: browserView.viewType === Maui.FMList.MILLERS_VIEW } } ] Loader { id: dialogLoader } Component { id: removeDialogComponent Maui.Dialog { property var urls: [] title: qsTr(String("Removing %1 files").arg(urls.length.toString())) message: Maui.Handy.isAndroid ? qsTr("This action will completely remove your files from your system. This action can not be undone.") : qsTr("You can move the file to the Trash or Delete it completely from your system. Which one you preffer?") rejectButton.text: qsTr("Delete") acceptButton.text: qsTr("Trash") - acceptButton.visible: !Kirigami.Settings.isMobile + acceptButton.visible: Maui.Handy.isLinux page.padding: Maui.Style.space.huge onRejected: { if(control.selectionBar && control.selectionBar.visible) { control.selectionBar.animate() control.clearSelection() } for(var i in urls) Maui.FM.removeFile(urls[i]) close() } onAccepted: { if(control.selectionBar && control.selectionBar.visible) { control.selectionBar.animate() control.clearSelection() } for(var i in urls) Maui.FM.moveToTrash(urls[i]) close() } } } Component { id: newFolderDialogComponent Maui.NewDialog { title: qsTr("New folder") message: qsTr("Create a new folder with a custom name") acceptButton.text: qsTr("Create") onFinished: control.currentFMList.createDir(text) rejectButton.visible: false textEntry.placeholderText: qsTr("Folder name...") } } Component { id: newFileDialogComponent Maui.NewDialog { title: qsTr("New file") message: qsTr("Create a new file with a custom name and extension") acceptButton.text: qsTr("Create") onFinished: Maui.FM.createFile(control.currentPath, text) rejectButton.visible: false textEntry.placeholderText: qsTr("File name...") } } Component { id: renameDialogComponent Maui.NewDialog { title: qsTr("Rename file") message: qsTr("Rename a file or folder to a new custom name") textEntry.text: itemMenu.item.label textEntry.placeholderText: qsTr("New name...") onFinished: Maui.FM.rename(itemMenu.item.path, textEntry.text) onRejected: close() acceptText: qsTr("Rename") rejectText: qsTr("Cancel") } } Component { id: shareDialogComponent MauiLab.ShareDialog {} } Component { id: tagsDialogComponent Maui.TagsDialog { taglist.strict: false onTagsReady: { composerList.updateToUrls(tags) if(control.previewer.visible) control.previewer.tagBar.list.refresh() } } } Component { id: _configDialogComponent Maui.Dialog { maxHeight: _configLayout.implicitHeight * 1.5 maxWidth: 300 defaultButtons: false Kirigami.FormLayout { id: _configLayout width: parent.width anchors.centerIn: parent Kirigami.Separator { Kirigami.FormData.label: qsTr("Navigation") Kirigami.FormData.isSection: true } Switch { icon.name: "image-preview" checkable: true checked: settings.showThumbnails Kirigami.FormData.label: qsTr("Thumbnails") onToggled: settings.showThumbnails = !settings.showThumbnails } Switch { Kirigami.FormData.label: qsTr("Hidden files") checkable: true checked: control.currentFMList.hidden onToggled: control.currentFMList.hidden = !control.currentFMList.hidden } Kirigami.Separator { Kirigami.FormData.label: qsTr("Others") Kirigami.FormData.isSection: true } Switch { Kirigami.FormData.label: qsTr("Status bar") checkable: true checked: control.footBar.visible - onToggled: control.footBar.visible = !control.footBar.visible + onToggled: + { + control.footBar.visible = !control.footBar.visible + Maui.FM.saveSettings("StatusBar", control.footBar.visible, "SETTINGS") + } } } } } Maui.FilePreviewer { id: previewer onShareButtonClicked: control.shareFiles([url]) } BrowserMenu { id: browserMenu } FileMenu { id: itemMenu width: Maui.Style.unit *200 onBookmarkClicked: control.bookmarkFolder([item.path]) onCopyClicked: { if(item) control.copy([item.path]) } onCutClicked: { if(item) control.cut([item.path]) } onTagsClicked: { if(item) { dialogLoader.sourceComponent = tagsDialogComponent dialog.composerList.urls = [item.path] dialog.open() } } onRenameClicked: { dialogLoader.sourceComponent = renameDialogComponent dialog.open() } onRemoveClicked: { console.log("REMOVE", item.path) control.remove([item.path]) } onShareClicked: control.shareFiles([item.path]) } Connections { target: browserView.currentView onKeyPress: { const index = browserView.currentView.currentIndex const item = control.currentFMList.get(index) // Shortcuts for refreshing if((event.key == Qt.Key_F5)) { control.currentFMList.refresh() } // Shortcuts for selecting file if((event.key == Qt.Key_A) && (event.modifiers & Qt.ControlModifier)) { control.selectAll() } if(event.key == Qt.Key_S) { if(control.selectionBar && control.selectionBar.contains(item.path)) { control.selectionBar.removeAtUri(item.path) }else { control.addToSelection(item) } } if((event.key == Qt.Key_Left || event.key == Qt.Key_Right || event.key == Qt.Key_Down || event.key == Qt.Key_Up) && (event.modifiers & Qt.ControlModifier) && (event.modifiers & Qt.ShiftModifier)) { if(control.selectionBar && control.selectionBar.contains(item.path)) { control.selectionBar.removeAtUri(item.path) }else { control.addToSelection(item) } } // Shortcut for pasting an item if((event.key == Qt.Key_V) && (event.modifiers & Qt.ControlModifier)) { control.paste(Maui.Handy.getClipboard().urls) } // Shortcut for cutting an item if((event.key == Qt.Key_X) && (event.modifiers & Qt.ControlModifier)) { var urls = [] if(control.selectionBar) { urls = control.selectionBar.uris } else { urls = [item.path] } control.cut(urls) } // Shortcut for copying an item if((event.key == Qt.Key_C) && (event.modifiers & Qt.ControlModifier)) { var urls = [] if(control.selectionBar) { urls = control.selectionBar.uris } else { urls = [item.path] } control.copy(urls) } // Shortcut for removing an item if(event.key == Qt.Key_Delete) { var urls = [] if(control.selectionBar) { urls = control.selectionBar.uris } else { urls = [item.path] } control.remove(urls) } // Shortcut for opening new tab if((event.key == Qt.Key_T) && (event.modifiers & Qt.ControlModifier)) { console.log("OPEN TAB") control.openTab(currentPath) } // Shortcut for closing tab if((event.key == Qt.Key_W) && (event.modifiers & Qt.ControlModifier)) { if(tabsBar.count > 1) control.closeTab(tabsBar.currentIndex) } // Shortcut for opening files in new tab , previewing or launching if((event.key == Qt.Key_Return) && (event.modifiers & Qt.ControlModifier)) { if(item.isdir == "true") control.openTab(item.path) }else if((event.key == Qt.Key_Return) && (event.modifiers & Qt.AltModifier)) { control.previewer.show(control.currentFMList.get(index).path) }else if(event.key == Qt.Key_Return) { indexHistory.push(index) control.itemClicked(index) } // Shortcut for going back in browsing history if(event.key == Qt.Key_Backspace || event.key == Qt.Key_Back) { if(control.selectionBar) control.clearSelection() else control.goBack() } // Shortcut for clearing selection if(event.key == Qt.Key_Escape) { if(control.selectionBar) control.clearSelection() } } onItemClicked: { browserView.currentView.currentIndex = index indexHistory.push(index) control.itemClicked(index) } onItemDoubleClicked: { browserView.currentView.currentIndex = index indexHistory.push(index) control.itemDoubleClicked(index) } onItemRightClicked: { if(control.currentFMList.pathType !== Maui.FMList.TRASH_PATH && control.currentFMList.pathType !== Maui.FMList.REMOTE_PATH) { itemMenu.show(index) } control.itemRightClicked(index) } onLeftEmblemClicked: { const item = control.currentFMList.get(index) if(control.selectionBar && control.selectionBar.contains(item.path)) { control.selectionBar.removeAtUri(item.path) }else { control.addToSelection(item) } control.itemLeftEmblemClicked(index) } onRightEmblemClicked: { Maui.Handy.isAndroid ? Maui.Android.shareDialog([control.currentFMList.get(index).path]) : shareDialog.show([control.currentFMList.get(index).path]) control.itemRightEmblemClicked(index) } onAreaClicked: { if(!Kirigami.Settings.isMobile && mouse.button === Qt.RightButton) browserMenu.show(control) else return control.rightClicked() } onAreaRightClicked: browserMenu.show(control) // onWarning: // { // notify("dialog-information", "An error happened", message) // } // onProgress: // { // if(percent === 100) // _progressBar.value = 0 // else // _progressBar.value = percent/100 // } } Component { id: selectionBarComponent MauiLab.SelectionBar { id: _selectionBar singleSelection: settings.singleSelection onCountChanged: { if(_selectionBar.count < 1) control.clearSelection() } onExitClicked: control.clearSelection() listDelegate: ListBrowserDelegate { Kirigami.Theme.inherit: true width: parent.width height: Maui.Style.iconSizes.big + Maui.Style.space.big label1.text: model.label label2.text: model.path showEmblem: true showThumbnails: true leftEmblem: "list-remove" folderSize: Maui.Style.iconSizes.big onLeftEmblemClicked: _selectionBar.removeAtIndex(index) keepEmblemOverlay: Maui.Handy.isTouch background: null onClicked: control.previewer.show(model.path) onPressAndHold: removeAtIndex(index) } Action { text: qsTr("Open") icon.name: "document-open" onTriggered: { if(control.selectionBar) { for(var i in uris) openFile(uris[i]) } } } Action { text: qsTr("Copy") icon.name: "edit-copy" onTriggered: if(control.selectionBar) { control.selectionBar.animate() control.copy(uris) } } Action { text: qsTr("Cut") icon.name: "edit-cut" onTriggered: if(control.selectionBar) { control.selectionBar.animate() control.cut(uris) } } Action { text: qsTr("Tags") icon.name: "tag" onTriggered: if(control.selectionBar) { dialogLoader.sourceComponent = tagsDialogComponent dialog.composerList.urls = uris dialog.open() } } Action { text: qsTr("Share") icon.name: "document-share" onTriggered: { control.shareFiles(uris) } } Action { text: qsTr("Remove") icon.name: "edit-delete" Kirigami.Theme.textColor: Kirigami.Theme.negativeTextColor onTriggered: { control.remove(uris) } } } } ObjectModel { id: tabsObjectModel } ColumnLayout { id: _layout anchors.fill: parent spacing: 0 Maui.TabBar { id: tabsBar visible: _browserList.count > 1 Layout.fillWidth: true Layout.preferredHeight: tabsBar.implicitHeight position: TabBar.Header currentIndex : _browserList.currentIndex ListModel { id: tabsListModel } Keys.onPressed: { if(event.key == Qt.Key_Return) { _browserList.currentIndex = currentIndex control.currentPath = tabsObjectModel.get(currentIndex).path } } Repeater { id: _repeater model: tabsListModel Maui.TabButton { id: _tabButton implicitHeight: tabsBar.implicitHeight implicitWidth: Math.max(control.width / _repeater.count, 120) checked: index === _browserList.currentIndex text: tabsObjectModel.get(index).currentFMList.pathName onClicked: { _browserList.currentIndex = index control.currentPath = tabsObjectModel.get(index).path } onCloseClicked: control.closeTab(index) } } } ListView { id: _browserList Layout.margins: 0 Layout.fillWidth: true Layout.fillHeight: true clip: true focus: true orientation: ListView.Horizontal model: tabsObjectModel snapMode: ListView.SnapOneItem spacing: 0 interactive: Maui.Handy.isTouch && tabsObjectModel.count > 1 highlightFollowsCurrentItem: true highlightMoveDuration: 0 onMovementEnded: _browserList.currentIndex = indexAt(contentX, contentY) // DropArea // { // id: _dropArea // anchors.fill: parent // z: parent.z -2 // onDropped: // { // const urls = drop.urls // for(var i in urls) // { // const item = Maui.FM.getFileInfo(urls[i]) // if(item.isdir == "true") // { // control.openTab(urls[i]) // } // } // } // } } Loader { id: selectionBarLoader Layout.alignment: Qt.AlignCenter Layout.margins: Maui.Style.space.medium Layout.preferredHeight: control.selectionBar && control.selectionBar.visible ? control.selectionBar.barHeight: 0 Layout.maximumWidth: 500 Layout.minimumWidth: 100 Layout.fillWidth: true Layout.bottomMargin: control.selectionBar && control.selectionBar.visible ? Maui.Style.contentMargins*2 : 0 } ProgressBar { id: _progressBar Layout.fillWidth: true Layout.alignment: Qt.AlignBottom Layout.preferredHeight: visible ? Maui.Style.iconSizes.medium : 0 visible: value > 0 } } Component.onCompleted: { openTab(Maui.FM.homePath()) browserView.currentView.forceActiveFocus() } onThumbnailsSizeChanged: { if(settings.trackChanges && settings.saveDirProps) Maui.FM.setDirConf(currentPath+"/.directory", "MAUIFM", "IconSize", thumbnailsSize) else Maui.FM.saveSettings("IconSize", thumbnailsSize, "SETTINGS") if(control.viewType === Maui.FMList.ICON_VIEW) browserView.currentView.adaptGrid() } function closeTab(index) { tabsObjectModel.remove(index) tabsListModel.remove(index) } function openTab(path) { if(path) { const component = Qt.createComponent("private/BrowserView.qml"); if (component.status === Component.Ready) { const object = component.createObject(tabsObjectModel, {'path': path}); tabsObjectModel.append(object) tabsListModel.append({"path": path}) _browserList.currentIndex = tabsObjectModel.count - 1 browserView.viewType = control.viewType control.currentPath = path } } } function shareFiles(urls) { if(urls.length <= 0) return; dialogLoader.sourceComponent= shareDialogComponent dialog.urls = urls dialog.open() } function openItem(index) { const item = control.currentFMList.get(index) const path = item.path switch(control.currentFMList.pathType) { case Maui.FMList.CLOUD_PATH: if(item.isdir === "true") { control.openFolder(path) } else { Maui.FM.openCloudItem(item) } break; default: if(settings.selectionMode && item.isdir == "false") { if(control.selectionBar && control.selectionBar.contains(item.path)) { control.selectionBar.removeAtPath(item.path) }else { control.addToSelection(item) } } else { if(item.isdir == "true") { control.openFolder(path) } else { if (Kirigami.Settings.isMobile) { control.previewer.show(path) } else { control.openFile(path) } } } } } function openFile(path) { Maui.FM.openUrl(path) } function openFolder(path) { if(!String(path).length) return; control.currentPath = path } function goBack() { openFolder(control.currentFMList.previousPath) // browserView.currentView.currentIndex = indexHistory.pop() } function goNext() { openFolder(control.currentFMList.posteriorPath) } function goUp() { openFolder(control.currentFMList.parentPath) } function refresh() { const pos = browserView.currentView.contentY browserView.currentView.contentY = pos } function addToSelection(item) { if(item.path.startsWith("tags://") || item.path.startsWith("applications://") ) return if(!control.selectionBar) selectionBarLoader.sourceComponent = selectionBarComponent control.selectionBar.append(item.path, item) } function clearSelection() { if(control.selectionBar) { control.selectionBar.clear() selectionBarLoader.sourceComponent = null settings.selectionMode = false } } function copy(urls) { Maui.Handy.copyToClipboard({"urls": urls}) control.isCut = false control.isCopy = true } function cut(urls) { Maui.Handy.copyToClipboard({"urls": urls}) control.isCut = true control.isCopy = false } function paste() { const urls = Maui.Handy.getClipboard().urls if(!urls) return if(control.isCut) { control.currentFMList.cutInto(urls) control.clearSelection() }else { control.currentFMList.copyInto(urls) } } function remove(urls) { dialogLoader.sourceComponent= removeDialogComponent dialog.urls = urls dialog.open() } function selectAll() //TODO for now dont select more than 100 items so things dont freeze or break { for(var i = 0; i < Math.min(control.currentFMList.count, 100); i++) addToSelection(control.currentFMList.get(i)) } function bookmarkFolder(paths) //multiple paths { control.newBookmark(paths) } function zoomIn() { control.thumbnailsSize = control.thumbnailsSize + 8 } function zoomOut() { const newSize = control.thumbnailsSize - 8 if(newSize >= Maui.Style.iconSizes.small) control.thumbnailsSize = newSize } function groupBy() { var prop = "" var criteria = ViewSection.FullString switch(control.currentFMList.sortBy) { case Maui.FMList.LABEL: prop = "label" criteria = ViewSection.FirstCharacter break; case Maui.FMList.MIME: prop = "mime" break; case Maui.FMList.SIZE: prop = "size" break; case Maui.FMList.DATE: prop = "date" break; case Maui.FMList.MODIFIED: prop = "modified" break; } if(!prop) { control.browserView.currentView.section.property = "" return } control.browserView.viewType = Maui.FMList.LIST_VIEW control.browserView.currentView.section.property = prop control.browserView.currentView.section.criteria = criteria } function openConfigDialog() { dialogLoader.sourceComponent = _configDialogComponent control.dialog.open() } } diff --git a/src/controls/ItemDelegate.qml b/src/controls/ItemDelegate.qml index bf72cbc..4d35964 100644 --- a/src/controls/ItemDelegate.qml +++ b/src/controls/ItemDelegate.qml @@ -1,155 +1,143 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.12 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import QtGraphicalEffects 1.0 import "private" ItemDelegate { id: control default property alias content : _content.data property alias mouseArea : _mouseArea // property alias tapArea : _tapArea property bool draggable: false property bool isCurrentItem : false property int radius: Maui.Style.radiusV highlighted: isCurrentItem //override the itemdelegate default signals to allow dragging content signal pressed(var mouse) signal pressAndHold(var mouse) signal clicked(var mouse) signal rightClicked(var mouse) signal doubleClicked(var mouse) Kirigami.Theme.inherit: false Kirigami.Theme.backgroundColor: "transparent" hoverEnabled: !Kirigami.Settings.isMobile padding: 0 bottomPadding: padding rightPadding: padding leftPadding: padding topPadding: padding MouseArea { id: _mouseArea // enabled: !Kirigami.Settings.isMobile anchors.fill: parent acceptedButtons: Qt.RightButton | Qt.LeftButton property int startX property int startY onClicked: { if(!Kirigami.Settings.isMobile && mouse.button === Qt.RightButton) control.rightClicked(mouse) else control.clicked(mouse) } onDoubleClicked: control.doubleClicked(mouse) onPressed: { if(control.draggable && mouse.source !== Qt.MouseEventSynthesizedByQt) { drag.target = control control.grabToImage(function(result) { parent.Drag.imageSource = result.url }) }else drag.target = null startX = control.x startY = control.y control.pressed(mouse) } onReleased : { control.x = startX control.y = startY } onPressAndHold : control.pressAndHold(mouse) } // TapHandler // { // id: _tapArea // enabled: Kirigami.Settings.isMobile // acceptedButtons: Qt.RightButton // onSingleTapped: control.clicked(eventPoint) // onDoubleTapped: control.doubleClicked(eventPoint) // onLongPressed: control.pressAndHold(eventPoint) // } - contentItem: null - - Item + contentItem: Item { - id: _content - - anchors - { - fill: control - topMargin: control.topPadding - bottomMargin: control.bottomPadding - leftMargin: control.leftPadding - rightMargin: control.rightPadding - margins: control.padding - } + id: _content } background: Rectangle { anchors { - fill: control + fill: parent topMargin: control.topPadding bottomMargin: control.bottomPadding leftMargin: control.leftPadding rightMargin: control.rightPadding margins: control.padding } Behavior on color { ColorAnimation { duration: Kirigami.Units.shortDuration } } color: control.isCurrentItem || control.hovered ? Qt.rgba(control.Kirigami.Theme.highlightColor.r, control.Kirigami.Theme.highlightColor.g, control.Kirigami.Theme.highlightColor.b, 0.2) : control.Kirigami.Theme.backgroundColor radius: control.radius border.color: control.isCurrentItem ? control.Kirigami.Theme.highlightColor : "transparent" } } diff --git a/src/controls/ListBrowser.qml b/src/controls/ListBrowser.qml index 1512755..02c55de 100644 --- a/src/controls/ListBrowser.qml +++ b/src/controls/ListBrowser.qml @@ -1,178 +1,177 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.10 import QtQuick.Controls 2.10 import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami Kirigami.ScrollablePage { id: control property int itemSize : Maui.Style.iconSizes.big property bool showEmblem : true property bool keepEmblemOverlay : false property string rightEmblem property string leftEmblem property bool showDetailsInfo : false property bool showPreviewThumbnails: true property alias model : _listView.model property alias delegate : _listView.delegate property alias section : _listView.section property alias contentY: _listView.contentY property alias currentIndex : _listView.currentIndex property alias currentItem : _listView.currentItem property alias count : _listView.count property alias cacheBuffer : _listView.cacheBuffer property alias topMargin: _listView.topMargin property alias bottomMargin: _listView.bottomMargin property alias rightMargin: _listView.rightMargin property alias leftMarging: _listView.leftMargin property alias listView: _listView property alias holder : _holder signal itemClicked(int index) signal itemDoubleClicked(int index) signal itemRightClicked(int index) signal rightEmblemClicked(int index) signal leftEmblemClicked(int index) signal areaClicked(var mouse) signal areaRightClicked() signal keyPress(var event) spacing: 0 focus: true padding: 0 leftPadding: padding rightPadding: padding topPadding: padding bottomPadding: padding Keys.enabled: false Kirigami.Theme.colorSet: Kirigami.Theme.View ListView { id: _listView - contentWidth: parent.width focus: true clip: true spacing: Maui.Style.space.tiny snapMode: ListView.NoSnap boundsBehavior: !Kirigami.Settings.isMobile? Flickable.StopAtBounds : Flickable.OvershootBounds interactive: Maui.Handy.isTouch highlightFollowsCurrentItem: true highlightMoveDuration: 0 highlightResizeDuration : 0 keyNavigationEnabled : bool keyNavigationWraps : bool Keys.onPressed: control.keyPress(event) // ScrollBar.vertical: ScrollBar { } Maui.Holder { id: _holder anchors.fill : parent } delegate: Maui.ListBrowserDelegate { id: delegate width: parent.width height: itemSize + Maui.Style.space.big leftPadding: Maui.Style.space.small rightPadding: Maui.Style.space.small padding: 0 showDetailsInfo: control.showDetailsInfo folderSize : itemSize showTooltip: true showEmblem: control.showEmblem keepEmblemOverlay : control.keepEmblemOverlay showThumbnails: showPreviewThumbnails rightEmblem: control.rightEmblem leftEmblem: control.leftEmblem Connections { target: delegate onClicked: { control.currentIndex = index control.itemClicked(index) } onDoubleClicked: { control.currentIndex = index control.itemDoubleClicked(index) } onPressAndHold: { control.currentIndex = index control.itemRightClicked(index) } onRightClicked: { control.currentIndex = index control.itemRightClicked(index) } onRightEmblemClicked: { control.currentIndex = index control.rightEmblemClicked(index) } onLeftEmblemClicked: { control.currentIndex = index control.leftEmblemClicked(index) } } } MouseArea { anchors.fill: parent z: -1 acceptedButtons: Qt.RightButton | Qt.LeftButton onClicked: { control.forceActiveFocus() control.areaClicked(mouse) } onPressAndHold: control.areaRightClicked() } } } diff --git a/src/controls/Page.qml b/src/controls/Page.qml index 71ba978..8cfe78e 100644 --- a/src/controls/Page.qml +++ b/src/controls/Page.qml @@ -1,134 +1,240 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.10 import QtQuick.Controls 2.10 import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami import QtQuick.Layouts 1.3 Page { id: control focus: true leftPadding: control.padding rightPadding: control.padding topPadding: control.padding bottomPadding: control.padding Kirigami.Theme.colorSet: Kirigami.Theme.View property bool showTitle : true + property Flickable flickable : null + property int footerPositioning : Kirigami.Settings.isMobile && flickable ? ListView.PullBackHeader : ListView.InlineFooter + property int headerPositioning : Kirigami.Settings.isMobile && flickable ? ListView.PullBackHeader : ListView.InlineHeader + signal goBackTriggered() signal goForwardTriggered() background: Rectangle { color: Kirigami.Theme.backgroundColor } + Connections + { + target: control.flickable ? control.flickable : null + enabled: control.flickable && (control.header || control.footer) + property int oldContentY + property bool updatingContentY: false + + onContentYChanged: + { + if(control.flickable.atYBeginning && !control.flickable.dragging) + { + control.header.y = 0 + control.footer.height = control.footer.implicitHeight + oldContentY = 0 + updatingContentY = false + return; + } + + if (updatingContentY || !control.flickable) + { + oldContentY = control.flickable.contentY; + return; + //TODO: merge + //if moves but not dragging, just update oldContentY + } else if (!control.flickable.dragging) + { + oldContentY = control.flickable.contentY; + return; + } + + + if (control.footerPositioning === ListView.InlineFooter && control.footer) + { + control.footer.height = control.footer.implicitHeight + + } else if (control.footerPositioning === ListView.PullBackFooter && control.footer) + { + var oldFHeight = control.footer.height; + + control.footer.height = Math.max(0, + Math.min(control.footer.implicitHeight, + control.footer.height + oldContentY - control.flickable.contentY)); + + //if the implicitHeight is changed, use that to simulate scroll + if (oldFHeight !== control.footer.height) { + updatingContentY = true; + updatingContentY = false; + } else { + oldContentY = control.flickable.contentY; + } + } + + if (control.headerPositioning === ListView.InlineHeader && control.header) + { + control.header.y = 0; + + } else if (control.headerPositioning === ListView.PullBackHeader && control.header) + { + var oldHY = control.header.y + + control.header.y = Math.max(-(control.header.implicitHeight), + Math.min(0, control.header.y + oldContentY - control.flickable.contentY)); + + if (oldHY !== control.header.y) { + updatingContentY = true; + control.flickable.contentY -= (oldHY - control.header.y); + updatingContentY = false; + } else { + oldContentY = control.flickable.contentY; + } + } + } + + onMovementEnded: + { + if (control.headerPositioning === ListView.PullBackHeader && control.header) + { + if (control.header.y > -(control.header.implicitHeight/2) ) + { + control.header.y = 0; + } else + { + control.header.y = -(control.header.implicitHeight) + } + } + + if (control.footerPositioning === ListView.PullBackFooter && control.header) + { + if (control.footer.height > (control.footer.implicitHeight/2) ) + { + control.footer.height = control.footer.implicitHeight + + } else + { + control.footer.height = 0 + } + } + } + } + property alias headBar : _headBar property alias footBar: _footBar property Maui.ToolBar mheadBar : Maui.ToolBar { id: _headBar - visible: count > 1 - width: control.width - height: implicitHeight + visible: count > 1 + width: control.width + height: implicitHeight + y position: ToolBar.Header + readonly property int preferredHeight: Maui.Style.toolBarHeight + Component { id: _titleComponent Label { text: control.title elide : Text.ElideRight font.bold : false font.weight: Font.Bold color : Kirigami.Theme.textColor font.pointSize: Maui.Style.fontSizes.big horizontalAlignment : Text.AlignHCenter verticalAlignment : Text.AlignVCenter } } middleContent: Loader { Layout.fillWidth: sourceComponent === _titleComponent Layout.fillHeight: sourceComponent === _titleComponent sourceComponent: control.title && control.showTitle ? _titleComponent : undefined } } property Maui.ToolBar mfootBar : Maui.ToolBar { id: _footBar - visible: count + visible: count position: ToolBar.Footer width: control.width height: implicitHeight } - + header: headBar.count && headBar.position === ToolBar.Header ? headBar : null footer: Column { id: _footer visible : children children: { if(headBar.position === ToolBar.Footer && headBar.count && footBar.count) return [footBar , headBar] else if(headBar.position === ToolBar.Footer && headBar.count) return [headBar] else if(footBar.count) return [footBar] else return [] } } Keys.onBackPressed: { control.goBackTriggered(); event.accepted = true } Shortcut { sequence: "Forward" onActivated: control.goForwardTriggered(); } Shortcut { sequence: StandardKey.Forward onActivated: control.goForwardTriggered(); } Shortcut { sequence: StandardKey.Back onActivated: control.goBackTriggered(); } } diff --git a/src/controls/private/BrowserView.qml b/src/controls/private/BrowserView.qml index 0e91253..8464bce 100644 --- a/src/controls/private/BrowserView.qml +++ b/src/controls/private/BrowserView.qml @@ -1,668 +1,666 @@ import QtQuick 2.9 import QtQuick.Controls 2.9 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui Maui.Page { id: control property url path onPathChanged: control.currentView.currentIndex = 0 property Maui.FMList currentFMList property Maui.BaseModel currentFMModel property alias currentView : viewLoader.item property int viewType property string filter height: _browserList.height width: _browserList.width function setCurrentFMList() { control.currentFMList = currentView.currentFMList control.currentFMModel = currentView.currentFMModel currentView.forceActiveFocus() } Menu { id: _dropMenu property string urls property url target enabled: Maui.FM.getFileInfo(target).isdir == "true" && !urls.includes(target.toString()) MenuItem { text: qsTr("Copy here") onTriggered: { const urls = _dropMenu.urls.split(",") for(var i in urls) Maui.FM.copy(urls[i], _dropMenu.target, false) } } MenuItem { text: qsTr("Move here") onTriggered: { const urls = _dropMenu.urls.split(",") for(var i in urls) Maui.FM.cut(urls[i], _dropMenu.target) } } MenuItem { text: qsTr("Link here") onTriggered: { const urls = _dropMenu.urls.split(",") for(var i in urls) Maui.FM.createSymlink(_dropMenu.source[i], urls.target) } } } Loader { id: viewLoader anchors.fill: parent focus: true sourceComponent: switch(control.viewType) { case Maui.FMList.ICON_VIEW: return gridViewBrowser case Maui.FMList.LIST_VIEW: return listViewBrowser case Maui.FMList.MILLERS_VIEW: return millerViewBrowser } onLoaded: setCurrentFMList() } Maui.FMList { id: _commonFMList path: control.path onSortByChanged: if(group) groupBy() onlyDirs: settings.onlyDirs filterType: settings.filterType filters: settings.filters sortBy: settings.sortBy } Component { id: listViewBrowser Maui.ListBrowser { id: _listViewBrowser property alias currentFMList : _browserModel.list property alias currentFMModel : _browserModel topMargin: Maui.Style.contentMargins showPreviewThumbnails: settings.showThumbnails keepEmblemOverlay: settings.selectionMode showDetailsInfo: true supportsRefreshing: true BrowserHolder { id: _holder browser: currentFMList } holder.visible: _holder.visible holder.emoji: _holder.emoji holder.title: _holder.title holder.body: _holder.body holder.emojiSize: _holder.emojiSize model: Maui.BaseModel { id: _browserModel list: _commonFMList filter: control.filter recursiveFilteringEnabled: true sortCaseSensitivity: Qt.CaseInsensitive filterCaseSensitivity: Qt.CaseInsensitive } section.delegate: Maui.LabelDelegate { id: delegate width: parent.width height: Maui.Style.toolBarHeightAlt label: String(section).toUpperCase() labelTxt.font.pointSize: Maui.Style.fontSizes.big isSection: true } delegate: Maui.ListBrowserDelegate { id: delegate width: parent.width height: _listViewBrowser.itemSize + Maui.Style.space.big leftPadding: Maui.Style.space.small - rightPadding: Maui.Style.space.small padding: 0 showDetailsInfo: _listViewBrowser.showDetailsInfo folderSize : _listViewBrowser.itemSize showTooltip: true showEmblem: _listViewBrowser.showEmblem keepEmblemOverlay : _listViewBrowser.keepEmblemOverlay showThumbnails: _listViewBrowser.showPreviewThumbnails rightEmblem: _listViewBrowser.rightEmblem isSelected: selectionBar ? selectionBar.contains(model.path) : false leftEmblem: isSelected ? "list-remove" : "list-add" draggable: true Maui.Badge { iconName: "link" anchors.left: parent.left anchors.bottom: parent.bottom visible: (model.issymlink == true) || (model.issymlink == "true") } Connections { target: selectionBar onUriRemoved: { if(uri === model.path) delegate.isSelected = false } onUriAdded: { if(uri === model.path) delegate.isSelected = true } onCleared: delegate.isSelected = false } Connections { target: delegate onClicked: { _listViewBrowser.currentIndex = index _listViewBrowser.itemClicked(index) } onDoubleClicked: { _listViewBrowser.currentIndex = index _listViewBrowser.itemDoubleClicked(index) } onPressAndHold: { _listViewBrowser.currentIndex = index _listViewBrowser.itemRightClicked(index) } onRightClicked: { _listViewBrowser.currentIndex = index _listViewBrowser.itemRightClicked(index) } onRightEmblemClicked: { _listViewBrowser.currentIndex = index _listViewBrowser.rightEmblemClicked(index) } onLeftEmblemClicked: { _listViewBrowser.currentIndex = index _listViewBrowser.leftEmblemClicked(index) } onContentDropped: { _dropMenu.urls = drop.urls.join(",") _dropMenu.target = model.path _dropMenu.popup() } } } } } Component { id: gridViewBrowser Maui.GridBrowser { id: _gridViewBrowser property alias currentFMList : _browserModel.list property alias currentFMModel : _browserModel itemSize : thumbnailsSize + Maui.Style.fontSizes.default cellHeight: itemSize * 1.5 keepEmblemOverlay: settings.selectionMode showPreviewThumbnails: settings.showThumbnails supportsRefreshing: true BrowserHolder { id: _holder browser: currentFMList } holder.visible: _holder.visible holder.emoji: _holder.emoji holder.title: _holder.title holder.body: _holder.body holder.emojiSize: _holder.emojiSize model: Maui.BaseModel { id: _browserModel list: _commonFMList filter: control.filter recursiveFilteringEnabled: true sortCaseSensitivity: Qt.CaseInsensitive filterCaseSensitivity: Qt.CaseInsensitive } delegate: Maui.GridBrowserDelegate { id: delegate folderSize: height * 0.5 height: _gridViewBrowser.cellHeight width: _gridViewBrowser.cellWidth padding: Maui.Style.space.tiny showTooltip: true showEmblem: _gridViewBrowser.showEmblem keepEmblemOverlay: _gridViewBrowser.keepEmblemOverlay showThumbnails: _gridViewBrowser.showPreviewThumbnails rightEmblem: _gridViewBrowser.rightEmblem isSelected: selectionBar ? selectionBar.contains(model.path) : false leftEmblem: isSelected ? "list-remove" : "list-add" draggable: true Maui.Badge { iconName: "link" anchors.left: parent.left anchors.bottom: parent.bottom anchors.bottomMargin: Maui.Style.space.big visible: (model.issymlink == true) || (model.issymlink == "true") } Connections { target: selectionBar onUriRemoved: { if(uri === model.path) delegate.isSelected = false } onUriAdded: { if(uri === model.path) delegate.isSelected = true } onCleared: delegate.isSelected = false } Connections { target: delegate onClicked: { _gridViewBrowser.currentIndex = index _gridViewBrowser.itemClicked(index) } onDoubleClicked: { _gridViewBrowser.currentIndex = index _gridViewBrowser.itemDoubleClicked(index) } onPressAndHold: { _gridViewBrowser.currentIndex = index _gridViewBrowser.itemRightClicked(index) } onRightClicked: { _gridViewBrowser.currentIndex = index _gridViewBrowser.itemRightClicked(index) } onRightEmblemClicked: { _gridViewBrowser.currentIndex = index _gridViewBrowser.rightEmblemClicked(index) } onLeftEmblemClicked: { _gridViewBrowser.currentIndex = index _gridViewBrowser.leftEmblemClicked(index) } onContentDropped: { _dropMenu.urls = drop.urls.join(",") _dropMenu.target = model.path _dropMenu.popup() } } } } } Component { id: millerViewBrowser Item { id: _millerControl property Maui.FMList currentFMList property Maui.BaseModel currentFMModel property int currentIndex signal itemClicked(int index) signal itemDoubleClicked(int index) signal itemRightClicked(int index) signal keyPress(var event) signal rightEmblemClicked(int index) signal leftEmblemClicked(int index) signal areaClicked(var mouse) signal areaRightClicked() ListView { id: _millerColumns anchors.fill: parent boundsBehavior: !Maui.Handy.isTouch? Flickable.StopAtBounds : Flickable.OvershootBounds keyNavigationEnabled: true interactive: Maui.Handy.isTouch orientation: ListView.Horizontal snapMode: ListView.SnapToItem ScrollBar.horizontal: ScrollBar { id: _scrollBar snapMode: ScrollBar.SnapAlways policy: ScrollBar.AlwaysOn contentItem: Rectangle { implicitWidth: _scrollBar.interactive ? 13 : 4 implicitHeight: _scrollBar.interactive ? 13 : 4 color: "#333" opacity: _scrollBar.pressed ? 0.7 : _scrollBar.interactive && _scrollBar.hovered ? 0.5 : 0.2 radius: 0 } background: Rectangle { implicitWidth: _scrollBar.interactive ? 16 : 4 implicitHeight: _scrollBar.interactive ? 16 : 4 color: "#0e000000" opacity: 0.0 visible: _scrollBar.interactive radius: 0 } } onCurrentItemChanged: { _millerControl.currentFMList = currentItem.currentFMList _millerControl.currentFMModel = currentItem.currentFMModel control.setCurrentFMList() currentItem.forceActiveFocus() } onCountChanged: { _millerColumns.currentIndex = _millerColumns.count-1 _millerColumns.positionViewAtEnd() } Maui.PathList { id: _millerList path: control.path onPathChanged: { _millerColumns.currentIndex = _millerColumns.count-1 _millerColumns.positionViewAtEnd() } } model: Maui.BaseModel { id: _millerModel list: _millerList } delegate: Item { property alias currentFMList : _millersFMList property alias currentFMModel : _millersFMModel property int _index : index width: Math.min(Kirigami.Units.gridUnit * 22, control.width) height: parent.height focus: true function forceActiveFocus() { _millerListView.forceActiveFocus() } Kirigami.Separator { anchors.top: parent.top anchors.bottom: parent.bottom anchors.right: parent.right width: 1 z: 999 } Maui.FMList { id: _millersFMList path: model.path onlyDirs: settings.onlyDirs filterType: settings.filterType filters: settings.filters sortBy: settings.sortBy } Maui.ListBrowser { id: _millerListView anchors.fill: parent topMargin: Maui.Style.contentMargins showPreviewThumbnails: settings.showThumbnails keepEmblemOverlay: settings.selectionMode showDetailsInfo: true onKeyPress: _millerControl.keyPress(event) currentIndex : 0 onCurrentIndexChanged: _millerControl.currentIndex = currentIndex BrowserHolder { id: _holder browser: currentFMList } holder.visible: _holder.visible holder.emoji: _holder.emoji holder.title: _holder.title holder.body: _holder.body holder.emojiSize: _holder.emojiSize section.delegate: Maui.LabelDelegate { id: delegate width: parent.width height: Maui.Style.toolBarHeightAlt label: String(section).toUpperCase() labelTxt.font.pointSize: Maui.Style.fontSizes.big isSection: true } onAreaClicked: { _millerColumns.currentIndex = _index _millerControl.areaClicked(mouse) } onAreaRightClicked: { _millerColumns.currentIndex = _index _millerControl.areaRightClicked() } model: Maui.BaseModel { id: _millersFMModel list: _millersFMList filter: control.filter recursiveFilteringEnabled: true sortCaseSensitivity: Qt.CaseInsensitive filterCaseSensitivity: Qt.CaseInsensitive } delegate: Maui.ListBrowserDelegate { id: delegate width: parent.width height: _millerListView.itemSize + Maui.Style.space.big leftPadding: Maui.Style.space.small - rightPadding: Maui.Style.space.small padding: 0 showDetailsInfo: _millerListView.showDetailsInfo folderSize : _millerListView.itemSize showTooltip: true showEmblem: _millerListView.showEmblem keepEmblemOverlay : _millerListView.keepEmblemOverlay showThumbnails: _millerListView.showPreviewThumbnails rightEmblem: _millerListView.rightEmblem isSelected: selectionBar ? selectionBar.contains(model.path) : false leftEmblem: isSelected ? "list-remove" : "list-add" draggable: true Maui.Badge { iconName: "link" anchors.left: parent.left anchors.bottom: parent.bottom visible: (model.issymlink == true) || (model.issymlink == "true") } Connections { target: selectionBar onUriRemoved: { if(uri === model.path) delegate.isSelected = false } onUriAdded: { if(uri === model.path) delegate.isSelected = true } onCleared: delegate.isSelected = false } Connections { target: delegate onClicked: { _millerColumns.currentIndex = _index _millerListView.currentIndex = index _millerControl.itemClicked(index) } onDoubleClicked: { _millerColumns.currentIndex = _index _millerListView.currentIndex = index _millerControl.itemDoubleClicked(index) } onPressAndHold: { _millerColumns.currentIndex = _index _millerListView.currentIndex = index _millerControl.itemRightClicked(index) } onRightClicked: { _millerColumns.currentIndex = _index _millerListView.currentIndex = index _millerControl.itemRightClicked(index) } onRightEmblemClicked: { _millerColumns.currentIndex = _index _millerListView.currentIndex = index _millerControl.rightEmblemClicked(index) } onLeftEmblemClicked: { _millerColumns.currentIndex = _index _millerListView.currentIndex = index _millerControl.leftEmblemClicked(index) } onContentDropped: { _dropMenu.urls = drop.urls.join(",") _dropMenu.target = model.path _dropMenu.popup() } } } } } } } } }