From 30012a0582b998b159d9de2dab1324e80e2d6b54 Mon Sep 17 00:00:00 2001 From: reionwong Date: Fri, 4 Sep 2026 06:31:07 -0400 Subject: [PATCH] feat(filemanager): refine sidebar and navigation layout --- helper/pathhistory.cpp | 10 ++ helper/pathhistory.h | 3 + model/foldermodel.cpp | 14 ++ model/foldermodel.h | 7 + qml/Controls/IconButton.qml | 11 +- qml/FolderPage.qml | 85 +-------- qml/PathBar.qml | 21 ++- qml/SideBar.qml | 348 ++++++++++++++++++++---------------- qml/main.qml | 89 +++++++-- 9 files changed, 339 insertions(+), 249 deletions(-) diff --git a/helper/pathhistory.cpp b/helper/pathhistory.cpp index fdc34cc..9593521 100644 --- a/helper/pathhistory.cpp +++ b/helper/pathhistory.cpp @@ -56,6 +56,16 @@ bool PathHistory::isEmpty() return m_prevHistory.isEmpty(); } +bool PathHistory::canGoBack() const +{ + return m_prevHistory.length() >= 2; +} + +bool PathHistory::canGoForward() const +{ + return !m_postHistory.isEmpty(); +} + QUrl PathHistory::posteriorPath() { if (m_postHistory.isEmpty()) diff --git a/helper/pathhistory.h b/helper/pathhistory.h index 78fa3c4..1ebd6fa 100644 --- a/helper/pathhistory.h +++ b/helper/pathhistory.h @@ -40,6 +40,9 @@ public: bool isEmpty(); + bool canGoBack() const; + bool canGoForward() const; + QUrl posteriorPath(); QUrl previousPath(); diff --git a/model/foldermodel.cpp b/model/foldermodel.cpp index ff6fa51..7901fd1 100644 --- a/model/foldermodel.cpp +++ b/model/foldermodel.cpp @@ -458,6 +458,8 @@ void FolderModel::setUrl(const QString &url) // Refresh this directory. if (url == m_url) { refresh(); + emit canGoBackChanged(); + emit canGoForwardChanged(); return; } @@ -479,6 +481,8 @@ void FolderModel::setUrl(const QString &url) emit urlChanged(); emit resolvedUrlChanged(); + emit canGoBackChanged(); + emit canGoForwardChanged(); } QUrl FolderModel::resolvedUrl() const @@ -637,6 +641,16 @@ bool FolderModel::dragging() const return m_dragInProgress; } +bool FolderModel::canGoBack() const +{ + return m_pathHistory.canGoBack(); +} + +bool FolderModel::canGoForward() const +{ + return m_pathHistory.canGoForward(); +} + bool FolderModel::isDir(const QModelIndex &index, const KDirModel *dirModel) const { KFileItem item = dirModel->itemForIndex(index); diff --git a/model/foldermodel.h b/model/foldermodel.h index 580f578..2cf980a 100644 --- a/model/foldermodel.h +++ b/model/foldermodel.h @@ -71,6 +71,8 @@ class FolderModel : public QSortFilterProxyModel, public QQmlParserStatus Q_PROPERTY(int archiveProgress READ archiveProgress NOTIFY archiveProgressChanged) Q_PROPERTY(QString archiveOperation READ archiveOperation NOTIFY archiveOperationChanged) Q_PROPERTY(QString archiveCurrentFile READ archiveCurrentFile NOTIFY archiveCurrentFileChanged) + Q_PROPERTY(bool canGoBack READ canGoBack NOTIFY canGoBackChanged) + Q_PROPERTY(bool canGoForward READ canGoForward NOTIFY canGoForwardChanged) public: enum DataRole { @@ -160,6 +162,9 @@ public: bool dragging() const; + bool canGoBack() const; + bool canGoForward() const; + bool isDir(const QModelIndex &index, const KDirModel *dirModel) const; bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; @@ -263,6 +268,8 @@ signals: void filterModeChanged(); void requestRename(); void draggingChanged(); + void canGoBackChanged(); + void canGoForwardChanged(); void viewAdapterChanged(); void isDesktopChanged(); void selectionCountChanged(); diff --git a/qml/Controls/IconButton.qml b/qml/Controls/IconButton.qml index df84f24..e25c636 100644 --- a/qml/Controls/IconButton.qml +++ b/qml/Controls/IconButton.qml @@ -38,8 +38,13 @@ Item { Rectangle { id: _background anchors.fill: parent - radius: FishUI.Theme.smallRadius - color: _mouseArea.pressed ? pressedColor : _mouseArea.containsMouse ? control.hoveredColor : control.backgroundColor + // Same inset as FishUI's window buttons, so both draw the same circle. + anchors.margins: Math.round(control.width * 0.1) + radius: width / 2 + color: !control.enabled ? "transparent" + : _mouseArea.pressed ? pressedColor + : _mouseArea.containsMouse ? control.hoveredColor + : control.backgroundColor } Image { @@ -50,11 +55,13 @@ Item { sourceSize: Qt.size(width, height) smooth: false antialiasing: true + opacity: control.enabled ? 1 : 0.35 } MouseArea { id: _mouseArea anchors.fill: parent + enabled: control.enabled hoverEnabled: true acceptedButtons: Qt.LeftButton onClicked: control.clicked() diff --git a/qml/FolderPage.qml b/qml/FolderPage.qml index 9adcbae..a07520f 100644 --- a/qml/FolderPage.qml +++ b/qml/FolderPage.qml @@ -33,8 +33,11 @@ Item { property alias currentUrl: dirModel.url property alias model: dirModel + property alias canGoBack: dirModel.canGoBack + property alias canGoForward: dirModel.canGoForward property Item currentView: _viewLoader.item - property int statusBarHeight: 22 + property int headerHeight: 0 + property int bottomNavigationHeight: 0 signal requestPathEditor() @@ -200,7 +203,9 @@ Item { ColumnLayout { anchors.fill: parent - anchors.bottomMargin: 2 + // Same breathing room the sidebar leaves between its title and its list. + anchors.topMargin: folderPage.headerHeight + anchors.bottomMargin: 2 + folderPage.bottomNavigationHeight spacing: 0 Loader { @@ -221,82 +226,6 @@ Item { shortCut.install(_viewLoader.item) } } - - Item { - visible: true - height: statusBarHeight - } - } - - Item { - id: _statusBar - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - height: statusBarHeight - z: 999 - -// Rectangle { -// anchors.fill: parent -// color: FishUI.Theme.backgroundColor -// opacity: 0.7 -// } - - MouseArea { - anchors.fill: parent - } - - RowLayout { - anchors.fill: parent - anchors.leftMargin: FishUI.Units.smallSpacing - anchors.rightMargin: FishUI.Units.smallSpacing - // anchors.bottomMargin: 1 - spacing: FishUI.Units.largeSpacing - - Label { - Layout.alignment: Qt.AlignLeft - font.pointSize: 10 - text: dirModel.count === 1 ? qsTr("%1 item").arg(dirModel.count) - : qsTr("%1 items").arg(dirModel.count) - } - - Label { - Layout.alignment: Qt.AlignLeft - font.pointSize: 10 - text: qsTr("%1 selected").arg(dirModel.selectionCount) - visible: dirModel.selectionCount >= 1 - } - - FishUI.BusyIndicator { - id: _busyIndicator - Layout.alignment: Qt.AlignLeft - height: statusBarHeight - width: height - running: visible - visible: dirModel.status === FM.FolderModel.Listing - } - - Label { - text: dirModel.selectedItemSize - visible: dirModel.url !== "trash:///" - } - - Item { - Layout.fillWidth: true - } - - Button { - id: _emptyTrashBtn - implicitHeight: statusBarHeight - text: qsTr("Empty Trash") - font.pointSize: 10 - onClicked: dirModel.emptyTrash() - visible: dirModel.url === "trash:///" - && _viewLoader.item - && _viewLoader.item.count > 0 - focusPolicy: Qt.NoFocus - } - } } function rename() { diff --git a/qml/PathBar.qml b/qml/PathBar.qml index c24b521..7915dbc 100644 --- a/qml/PathBar.qml +++ b/qml/PathBar.qml @@ -29,11 +29,30 @@ Item { id: control property string url: "" + property string currentName: nameForUrl(url) signal itemClicked(string path) signal editorAccepted(string path) - // The path bar is part of the title bar. Start the Wayland move only + function nameForUrl(value) { + if (!value) + return "" + + if (value.indexOf("trash:/") === 0) + return qsTr("Trash") + + var path = value.toString() + if (path.indexOf("file://") === 0) + path = path.substring(7) + + while (path.length > 1 && path.endsWith("/")) + path = path.substring(0, path.length - 1) + + var separator = path.lastIndexOf("/") + return separator >= 0 ? (path.substring(separator + 1) || "/") : path + } + + // The path bar is part of the navigation bar. Start the Wayland move only // after a drag begins, so a single click can open the path editor. property bool _dragged: false property real _pressX: 0 diff --git a/qml/SideBar.qml b/qml/SideBar.qml index 245dca5..a0470bb 100644 --- a/qml/SideBar.qml +++ b/qml/SideBar.qml @@ -25,23 +25,20 @@ import QtQuick.Window 2.12 import FishUI 1.0 as FishUI import Cutefish.FileManager 1.0 -ListView { +Item { id: sideBar - signal clicked(string path) - signal openInNewWindow(string path) + implicitWidth: 190 - // Rounding to whole *logical* pixels is not enough at a fractional scale: - // at 150% a 37px row pitch is 55.5 device px, so every other row lands on - // a half pixel and its icon is resampled. - readonly property real dpr: FishUI.Dpi.ratio - function snap(v) { - return Math.round(v * sideBar.dpr) / sideBar.dpr - } + property string title + // The header aligns its own contents to this label's text line. + property alias titleItem: titleLabel + property alias currentIndex: listView.currentIndex + property alias count: listView.count + property alias model: listView.model - FishUI.WheelHandler { - target: sideBar - } + signal clicked(string path) + signal openInNewWindow(string path) Fm { id: _fm @@ -52,170 +49,219 @@ ListView { onDeviceSetupDone: sideBar.clicked(filePath) // 设备挂载上后,模拟点击了该设备以打开该页面 } - model: placesModel - clip: true - - // Not leftMargin/rightMargin: Qt rounds the content item's position to a - // whole logical pixel, so a snapped fractional margin never takes effect. - // The inset is applied inside the rows instead. - readonly property real sideInset: sideBar.snap(FishUI.Units.smallSpacing * 1.5) - - bottomMargin: sideBar.snap(FishUI.Units.smallSpacing) - spacing: sideBar.snap(FishUI.Units.smallSpacing / 2) + Rectangle { + anchors.fill: parent + color: FishUI.Theme.darkMode ? Qt.lighter(FishUI.Theme.backgroundColor, 1.5) + : Qt.darker(FishUI.Theme.backgroundColor, 1.05) + opacity: 0.7 - ScrollBar.vertical: ScrollBar { - bottomPadding: FishUI.Units.smallSpacing - } - - section.property: "category" - section.delegate: Item { - width: ListView.view.width - height: sideBar.snap(FishUI.Units.fontMetrics.height + FishUI.Units.largeSpacing + FishUI.Units.smallSpacing) - - Text { - anchors.left: parent.left - anchors.top: parent.top - anchors.leftMargin: Qt.application.layoutDirection === Qt.RightToLeft - ? 0 : sideBar.snap(sideBar.sideInset + FishUI.Units.smallSpacing) - anchors.rightMargin: FishUI.Units.smallSpacing - anchors.topMargin: FishUI.Units.largeSpacing - anchors.bottomMargin: FishUI.Units.smallSpacing - color: FishUI.Theme.textColor - font.pointSize: 9 - font.bold: true - text: section + Behavior on color { + ColorAnimation { + duration: 250 + easing.type: Easing.Linear + } } } - delegate: Item { - id: _item - width: ListView.view.width - height: sideBar.snap(FishUI.Units.fontMetrics.height + FishUI.Units.largeSpacing * 1.5) - - property bool checked: sideBar.currentIndex === index - property color hoveredColor: FishUI.Theme.darkMode ? Qt.lighter(FishUI.Theme.backgroundColor, 1.1) - : Qt.darker(FishUI.Theme.backgroundColor, 1.1) - MouseArea { - id: _mouseArea - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: function(mouse) { - if (mouse.button === Qt.LeftButton) { - if (model.isDevice && model.setupNeeded) - placesModel.requestSetup(index) - else - sideBar.clicked(model.path ? model.path : model.url) - } else if (mouse.button === Qt.RightButton) { - _menu.popup() - } - } - } + ColumnLayout { + anchors.fill: parent + spacing: 0 - FishUI.DesktopMenu { - id: _menu + Label { + id: titleLabel + text: sideBar.title + color: root.active ? FishUI.Theme.textColor : FishUI.Theme.disabledTextColor + Layout.preferredHeight: root.header.height + leftPadding: FishUI.Units.largeSpacing + FishUI.Units.smallSpacing + rightPadding: FishUI.Units.largeSpacing + FishUI.Units.smallSpacing + topPadding: FishUI.Units.smallSpacing + bottomPadding: 0 + font.pointSize: 12 + } - FishUI.MenuItem { - text: qsTr("Open") + ListView { + id: listView + Layout.fillHeight: true + Layout.fillWidth: true + Layout.topMargin: FishUI.Units.smallSpacing + clip: true + model: placesModel + + // Rounding to whole *logical* pixels is not enough at a fractional scale: + // at 150% a 37px row pitch is 55.5 device px, so every other row lands on + // a half pixel and its icon is resampled. + readonly property real dpr: FishUI.Dpi.ratio + function snap(v) { + return Math.round(v * listView.dpr) / listView.dpr + } - onTriggered: { - if (model.isDevice && model.setupNeeded) - placesModel.requestSetup(index) - else - sideBar.clicked(model.path ? model.path : model.url) - } + FishUI.WheelHandler { + target: listView } - FishUI.MenuItem { - text: qsTr("Open in new window") + // Not leftMargin/rightMargin: Qt rounds the content item's position to a + // whole logical pixel, so a snapped fractional margin never takes effect. + // The inset is applied inside the rows instead. + readonly property real sideInset: listView.snap(FishUI.Units.smallSpacing * 1.5) - onTriggered: { - sideBar.openInNewWindow(model.path ? model.path : model.url) - } - } + bottomMargin: listView.snap(FishUI.Units.smallSpacing) + spacing: 2 - FishUI.MenuSeparator { - Layout.fillWidth: true - visible: _ejectMenuItem.visible || _umountMenuItem.visible + ScrollBar.vertical: ScrollBar { + bottomPadding: FishUI.Units.smallSpacing } - FishUI.MenuItem { - id: _ejectMenuItem - text: qsTr("Eject") - visible: model.isDevice && - !model.setupNeeded && - model.isOpticalDisc && - !model.url.toString() === _fm.rootPath() - - onTriggered: { - placesModel.requestEject(index) + section.property: "category" + section.delegate: Item { + width: ListView.view.width + height: listView.snap(FishUI.Units.fontMetrics.height + FishUI.Units.largeSpacing + FishUI.Units.smallSpacing) + + Text { + anchors.left: parent.left + anchors.top: parent.top + anchors.leftMargin: Qt.application.layoutDirection === Qt.RightToLeft + ? 0 : listView.snap(listView.sideInset + FishUI.Units.smallSpacing) + anchors.rightMargin: FishUI.Units.smallSpacing + anchors.topMargin: FishUI.Units.largeSpacing + anchors.bottomMargin: FishUI.Units.smallSpacing + color: FishUI.Theme.textColor + font.pointSize: 9 + font.bold: true + text: section } } - FishUI.MenuItem { - id: _umountMenuItem - text: qsTr("Unmount") - visible: model.isDevice && - !model.setupNeeded && - !model.isOpticalDisc && - !model.url.toString() === _fm.rootPath() + delegate: Item { + id: _item + width: ListView.view.width + height: listView.snap(FishUI.Units.fontMetrics.height + FishUI.Units.largeSpacing * 1.5) + + property bool checked: sideBar.currentIndex === index + property color hoveredColor: FishUI.Theme.darkMode ? Qt.lighter(FishUI.Theme.backgroundColor, 1.1) + : Qt.darker(FishUI.Theme.backgroundColor, 1.1) + MouseArea { + id: _mouseArea + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: function(mouse) { + if (mouse.button === Qt.LeftButton) { + if (model.isDevice && model.setupNeeded) + placesModel.requestSetup(index) + else + sideBar.clicked(model.path ? model.path : model.url) + } else if (mouse.button === Qt.RightButton) { + _menu.popup() + } + } + } - onTriggered: { - placesModel.requestTeardown(index) + FishUI.DesktopMenu { + id: _menu + + FishUI.MenuItem { + text: qsTr("Open") + + onTriggered: { + if (model.isDevice && model.setupNeeded) + placesModel.requestSetup(index) + else + sideBar.clicked(model.path ? model.path : model.url) + } + } + + FishUI.MenuItem { + text: qsTr("Open in new window") + + onTriggered: { + sideBar.openInNewWindow(model.path ? model.path : model.url) + } + } + + FishUI.MenuSeparator { + Layout.fillWidth: true + visible: _ejectMenuItem.visible || _umountMenuItem.visible + } + + FishUI.MenuItem { + id: _ejectMenuItem + text: qsTr("Eject") + visible: model.isDevice && + !model.setupNeeded && + model.isOpticalDisc && + !model.url.toString() === _fm.rootPath() + + onTriggered: { + placesModel.requestEject(index) + } + } + + FishUI.MenuItem { + id: _umountMenuItem + text: qsTr("Unmount") + visible: model.isDevice && + !model.setupNeeded && + !model.isOpticalDisc && + !model.url.toString() === _fm.rootPath() + + onTriggered: { + placesModel.requestTeardown(index) + } + } } - } - } - Rectangle { - x: sideBar.sideInset - width: _item.width - sideBar.sideInset * 2 - height: parent.height - radius: FishUI.Theme.mediumRadius - color: _mouseArea.pressed ? Qt.rgba(FishUI.Theme.textColor.r, - FishUI.Theme.textColor.g, - FishUI.Theme.textColor.b, FishUI.Theme.darkMode ? 0.05 : 0.1) : - _mouseArea.containsMouse && !checked ? Qt.rgba(FishUI.Theme.textColor.r, - FishUI.Theme.textColor.g, - FishUI.Theme.textColor.b, FishUI.Theme.darkMode ? 0.1 : 0.05) : - checked ? Qt.rgba(FishUI.Theme.textColor.r, - FishUI.Theme.textColor.g, - FishUI.Theme.textColor.b, 0.05) : "transparent" - - smooth: true - } + Rectangle { + x: listView.sideInset + width: _item.width - listView.sideInset * 2 + height: parent.height + radius: FishUI.Theme.mediumRadius + color: _mouseArea.pressed ? Qt.rgba(FishUI.Theme.textColor.r, + FishUI.Theme.textColor.g, + FishUI.Theme.textColor.b, FishUI.Theme.darkMode ? 0.05 : 0.1) : + _mouseArea.containsMouse && !checked ? Qt.rgba(FishUI.Theme.textColor.r, + FishUI.Theme.textColor.g, + FishUI.Theme.textColor.b, FishUI.Theme.darkMode ? 0.1 : 0.05) : + checked ? Qt.rgba(FishUI.Theme.textColor.r, + FishUI.Theme.textColor.g, + FishUI.Theme.textColor.b, 0.05) : "transparent" + + smooth: true + } - // Positioned by hand rather than with a RowLayout: AlignVCenter computes - // (height - 22) / 2, which lands on a half device pixel even when the - // row height itself is snapped. - FishUI.IconItem { - id: _icon - x: sideBar.snap(sideBar.sideInset + FishUI.Units.smallSpacing) - y: sideBar.snap((_item.height - height) / 2) - width: sideBar.snap(22) - height: width - - source: "qrc:/images/" + model.iconPath - color: checked ? FishUI.Theme.highlightColor : FishUI.Theme.textColor - } + // Positioned by hand rather than with a RowLayout: AlignVCenter computes + // (height - 22) / 2, which lands on a half device pixel even when the + // row height itself is snapped. + FishUI.IconItem { + id: _icon + x: listView.snap(listView.sideInset + FishUI.Units.smallSpacing) + y: listView.snap((_item.height - height) / 2) + width: listView.snap(22) + height: width + + source: "qrc:/images/" + model.iconPath + color: checked ? FishUI.Theme.highlightColor : FishUI.Theme.textColor + } - Label { - id: _label - x: sideBar.snap(_icon.x + _icon.width + FishUI.Units.smallSpacing) - y: sideBar.snap((_item.height - height) / 2) - width: _item.width - x - sideBar.snap(FishUI.Units.smallSpacing) - text: model.name - color: checked ? FishUI.Theme.highlightColor : FishUI.Theme.textColor - elide: Text.ElideRight + Label { + id: _label + x: listView.snap(_icon.x + _icon.width + FishUI.Units.smallSpacing) + y: listView.snap((_item.height - height) / 2) + width: _item.width - x - listView.snap(FishUI.Units.smallSpacing) + text: model.name + color: checked ? FishUI.Theme.highlightColor : FishUI.Theme.textColor + elide: Text.ElideRight + } + } } } function updateSelection(path) { - sideBar.currentIndex = -1 + listView.currentIndex = -1 - for (var i = 0; i < sideBar.count; ++i) { - if (path === sideBar.model.get(i).path || - path === sideBar.model.get(i).url) { - sideBar.currentIndex = i + for (var i = 0; i < listView.count; ++i) { + if (path === listView.model.get(i).path || + path === listView.model.get(i).url) { + listView.currentIndex = i break } } diff --git a/qml/main.qml b/qml/main.qml index 1f43492..723ff3b 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -35,7 +35,8 @@ FishUI.Window { title: qsTr("File Manager") background.opacity: 1 - header.height: 36 + FishUI.Units.largeSpacing + header.height: 40 + contentTopMargin: 0 LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true @@ -62,41 +63,57 @@ FishUI.Window { headerItem: Item { RowLayout { + id: _headerRow anchors.fill: parent - anchors.leftMargin: FishUI.Units.smallSpacing * 1.5 - anchors.rightMargin: FishUI.Units.smallSpacing * 1.5 - anchors.topMargin: FishUI.Units.smallSpacing * 1.5 - anchors.bottomMargin: FishUI.Units.smallSpacing * 1.5 - + anchors.leftMargin: _sideBar.width + FishUI.Units.smallSpacing * 1.5 + anchors.rightMargin: FishUI.Units.smallSpacing spacing: FishUI.Units.smallSpacing + readonly property int buttonSize: 31 + + FontMetrics { + id: _nameMetrics + font: _folderName.font + } + IconButton { - Layout.fillHeight: true - implicitWidth: height + Layout.alignment: root.windowButtonsAlignment + Layout.topMargin: root.windowButtonsTopMargin + Layout.preferredWidth: _headerRow.buttonSize + Layout.preferredHeight: _headerRow.buttonSize + enabled: _folderPage.canGoBack source: FishUI.Theme.darkMode ? "qrc:/images/dark/go-previous.svg" : "qrc:/images/light/go-previous.svg" onClicked: _folderPage.goBack() } IconButton { - Layout.fillHeight: true - implicitWidth: height + Layout.alignment: root.windowButtonsAlignment + Layout.topMargin: root.windowButtonsTopMargin + Layout.preferredWidth: _headerRow.buttonSize + Layout.preferredHeight: _headerRow.buttonSize + enabled: _folderPage.canGoForward source: FishUI.Theme.darkMode ? "qrc:/images/dark/go-next.svg" : "qrc:/images/light/go-next.svg" onClicked: _folderPage.goForward() } - PathBar { - id: _pathBar + Label { + id: _folderName Layout.fillWidth: true Layout.fillHeight: true - onItemClicked: _folderPage.openUrl(path) - onEditorAccepted: _folderPage.openUrl(path) + leftPadding: root.windowButtonsTopMargin + topPadding: root.windowButtonsTopMargin / 2 + color: root.active ? FishUI.Theme.textColor : FishUI.Theme.disabledTextColor + font.pointSize: 11 + text: _pathBar.currentName } IconButton { - Layout.fillHeight: true - implicitWidth: height + Layout.alignment: root.windowButtonsAlignment + Layout.topMargin: root.windowButtonsTopMargin + Layout.preferredWidth: _headerRow.buttonSize + Layout.preferredHeight: _headerRow.buttonSize property var gridSource: FishUI.Theme.darkMode ? "qrc:/images/dark/grid.svg" : "qrc:/images/light/grid.svg" property var listSource: FishUI.Theme.darkMode ? "qrc:/images/dark/list.svg" : "qrc:/images/light/list.svg" @@ -117,7 +134,7 @@ FishUI.Window { SideBar { id: _sideBar Layout.fillHeight: true - width: 180 + FishUI.Units.largeSpacing + title: root.title onClicked: _folderPage.openUrl(path) onOpenInNewWindow: _folderPage.model.openInNewWindow(path) } @@ -126,6 +143,8 @@ FishUI.Window { id: _folderPage Layout.fillWidth: true Layout.fillHeight: true + headerHeight: root.header.height + bottomNavigationHeight: root.header.height onCurrentUrlChanged: { _sideBar.updateSelection(currentUrl) _pathBar.updateUrl(currentUrl) @@ -135,4 +154,40 @@ FishUI.Window { } } } + + Item { + id: _navigationBar + x: _folderPage.x + width: _folderPage.width + height: root.header.height + anchors.bottom: parent.bottom + z: 3 + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + height: 1 + color: Qt.rgba(FishUI.Theme.textColor.r, + FishUI.Theme.textColor.g, + FishUI.Theme.textColor.b, FishUI.Theme.darkMode ? 0.16 : 0.12) + } + + RowLayout { + anchors.fill: parent + anchors.leftMargin: FishUI.Units.smallSpacing * 1.5 + anchors.rightMargin: FishUI.Units.smallSpacing * 1.5 + anchors.topMargin: FishUI.Units.smallSpacing + anchors.bottomMargin: FishUI.Units.smallSpacing + spacing: 0 + + PathBar { + id: _pathBar + Layout.fillWidth: true + Layout.fillHeight: true + onItemClicked: _folderPage.openUrl(path) + onEditorAccepted: _folderPage.openUrl(path) + } + } + } }