diff --git a/src/dfm-declarative/AnimationHSpliter.qml b/src/dfm-declarative/AnimationHSpliter.qml new file mode 100644 index 0000000000..764698552a --- /dev/null +++ b/src/dfm-declarative/AnimationHSpliter.qml @@ -0,0 +1,127 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +import QtQuick +import QtQuick.Layouts + +/*! + * 用于边栏的横向分裂器,提供拖拽控件,动画交互功能 + */ +Item { + id: spliter + + // 是否响应动画触发事件 + property alias enableAnimation: trans.enabled + // 是否响应 Hover 和拖拽效果 + property alias enableMouse: mouseLoader.active + property alias handle: handleLoader.sourceComponent + property int handleWidth: 6 + // 分裂器附加 anchor 位置,允许在父控件左侧或右侧的边缘,计算方向相反 + property bool leftSide: false + property real maximumWidth: 600 + property real minimumWidth: 150 + // 缓存手动拖拽的宽度,用于动画恢复 + property real showWidth: 200 + // 动画控制切换,switchShow 更新为 true 时触发弹出动画,为 false 时触发隐藏动画 + property bool switchShow: true + property Item target: parent + + function adjustParentWidth(xOffset) { + var adjWidth; + if (null === spliter.target) { + return; + } + adjWidth = spliter.target.width + (leftSide ? -xOffset : xOffset); + adjWidth = Math.min(Math.max(minimumWidth, adjWidth), maximumWidth); + // 缓存用于动画的宽度 + showWidth = adjWidth; + // 外部组件可能被布局管理 + if (null !== target.Layout) { + target.Layout.preferredWidth = adjWidth; + } else { + target.width = adjWidth; + } + } + + implicitHeight: target ? target.height : 0 + implicitWidth: handleWidth + + // 拖拽也会触发属性变更,因此未使用 Behaviour + states: [ + State { + name: "show" + when: spliter.switchShow + + PropertyChanges { + Layout.preferredWidth: showWidth + explicit: true + target: spliter.target + } + }, + State { + name: "hide" + when: !spliter.switchShow + + PropertyChanges { + Layout.preferredWidth: 0 + explicit: true + target: spliter.target + } + } + ] + transitions: Transition { + id: trans + + onRunningChanged: { + if (running && spliter.switchShow) { + spliter.target.visible = true; + } else if (!running && !spliter.switchShow) { + spliter.target.visible = false; + } + } + + NumberAnimation { + duration: 200 + easing.type: Easing.InOutQuad + property: "Layout.preferredWidth" + } + } + + anchors { + left: leftSide ? target.left : undefined + right: leftSide ? undefined : target.right + } + + Loader { + id: handleLoader + + sourceComponent: Item { + height: spliter.height + width: handleWidth + } + } + + Loader { + id: mouseLoader + + anchors.fill: parent + + sourceComponent: MouseArea { + property real baseX + + anchors.fill: parent + cursorShape: (containsMouse || pressed) ? Qt.SplitHCursor : Qt.ArrowCursor + hoverEnabled: true + + onPositionChanged: { + if (pressed) { + spliter.adjustParentWidth(mouseX - baseX); + } + } + onPressed: { + baseX = mouseX; + } + } + } +} diff --git a/src/dfm-declarative/CMakeLists.txt b/src/dfm-declarative/CMakeLists.txt index 8b10c04286..8f84e4de94 100644 --- a/src/dfm-declarative/CMakeLists.txt +++ b/src/dfm-declarative/CMakeLists.txt @@ -38,7 +38,7 @@ qt_add_qml_module(${BIN_NAME} SHARED RESOURCE_PREFIX / OUTPUT_DIRECTORY ${DFM_BUILD_PLUGIN_DIR}/qml/org/dfm/${BASE_NAME} - QML_FILES ActionMenu.qml + QML_FILES ActionMenu.qml AnimationHSpliter.qml ) target_include_directories(${BIN_NAME} PRIVATE ${PROJECT_SOURCE_DIR}) diff --git a/src/plugins/filemanager/core/dfmplugin-core/FileWindow.qml b/src/plugins/filemanager/core/dfmplugin-core/FileWindow.qml index 7496811be8..286bbd11be 100644 --- a/src/plugins/filemanager/core/dfmplugin-core/FileWindow.qml +++ b/src/plugins/filemanager/core/dfmplugin-core/FileWindow.qml @@ -13,9 +13,11 @@ import org.deepin.dtk ApplicationWindow { id: root - DWindow.enabled: false + DWindow.enabled: true flags: Qt.Window | Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint height: 600 + minimumHeight: 380 + minimumWidth: 600 width: 800 // TODO: 待评估方案 @@ -24,10 +26,9 @@ ApplicationWindow { return; } if (undefined !== appletItem.widgetType) { - var control; switch (appletItem.widgetType) { case QuickUtils.Sidebar: - sidebar.contentItem = appletItem; + sidebar.target = appletItem; break; case QuickUtils.Titlebar: titlebar.target = appletItem; @@ -50,24 +51,73 @@ ApplicationWindow { // For local module test ActionMenu { } - SplitView { + + Connections { + function onWidthChanged(width) { + } + + enabled: Window.window !== null + target: Window.window + } + + RowLayout { anchors.fill: parent - Control { - id: sidebar + Item { + id: sidebarProxy + + Layout.preferredHeight: parent.height - contentItem: Rectangle { - width: 200 + ColumnLayout { + id: sidebarLayoutContent + + anchors.fill: parent + + // 用于同步标题栏高度占位的区块 + Rectangle { + id: titlebarCorner + + Layout.preferredHeight: titlebar.target ? titlebar.target.topHeaderHeight : titlebar.height + Layout.preferredWidth: parent.width + color: "lightyellow" + } + + LayoutItemProxy { + id: sidebar + + Layout.fillHeight: true + } + } + + AnimationHSpliter { + id: spliter + + enableAnimation: sidebar.target !== null + height: parent.height + target: sidebarProxy + + // TODO 移动到 Sidebar, 通过事件处理,而不是 QML 隐式传输 + Connections { + function onSidebarVisibleNotify(bVisible) { + spliter.switchShow = bVisible; + } + + enabled: titlebar.target !== null + target: titlebar.target + } } } + ColumnLayout { - SplitView.fillHeight: true - SplitView.fillWidth: true + Layout.fillHeight: true + Layout.fillWidth: true LayoutItemProxy { id: titlebar + onTargetChanged: target => {} } + RowLayout { Layout.fillHeight: true Layout.fillWidth: true @@ -76,6 +126,7 @@ ApplicationWindow { id: workspace } + LayoutItemProxy { id: detailspace diff --git a/src/plugins/filemanager/core/dfmplugin-titlebar/events/titlebareventcaller.cpp b/src/plugins/filemanager/core/dfmplugin-titlebar/events/titlebareventcaller.cpp index 261c031553..57027a0c21 100644 --- a/src/plugins/filemanager/core/dfmplugin-titlebar/events/titlebareventcaller.cpp +++ b/src/plugins/filemanager/core/dfmplugin-titlebar/events/titlebareventcaller.cpp @@ -99,6 +99,13 @@ ViewMode TitleBarEventCaller::sendGetDefualtViewMode(const QString &scheme) return static_cast(defaultViewMode); } +void TitleBarEventCaller::sendDetailViewState(dfmgui::Applet *applet, bool checked) +{ + quint64 id = TitleBarHelper::windowId(applet); + Q_ASSERT(id > 0); + dpfSlotChannel->push("dfmplugin_detailspace", "slot_DetailView_Show", id, checked); +} + void TitleBarEventCaller::sendCd(dfmgui::Applet *applet, const QUrl &url) { DFMBASE_USE_NAMESPACE diff --git a/src/plugins/filemanager/core/dfmplugin-titlebar/events/titlebareventcaller.h b/src/plugins/filemanager/core/dfmplugin-titlebar/events/titlebareventcaller.h index d184bf9164..ab93e1af39 100644 --- a/src/plugins/filemanager/core/dfmplugin-titlebar/events/titlebareventcaller.h +++ b/src/plugins/filemanager/core/dfmplugin-titlebar/events/titlebareventcaller.h @@ -33,6 +33,7 @@ class TitleBarEventCaller static bool sendCheckTabAddable(quint64 windowId); static DFMGLOBAL_NAMESPACE::ViewMode sendGetDefualtViewMode(const QString &scheme); + static void sendDetailViewState(dfmgui::Applet *applet, bool checked); static void sendCd(dfmgui::Applet *applet, const QUrl &url); }; diff --git a/src/plugins/filemanager/core/dfmplugin-titlebar/qml/LineSearch.qml b/src/plugins/filemanager/core/dfmplugin-titlebar/qml/LineSearch.qml index f99714d9f3..d0fa6a673c 100644 --- a/src/plugins/filemanager/core/dfmplugin-titlebar/qml/LineSearch.qml +++ b/src/plugins/filemanager/core/dfmplugin-titlebar/qml/LineSearch.qml @@ -6,8 +6,17 @@ import QtQuick import QtQuick.Controls TextEdit { + property bool visibleState: true + width: 200 + Behavior on width { + NumberAnimation { + duration: 200 + easing.type: Easing.InOutQuad + } + } + TextField { anchors.fill: parent horizontalAlignment: TextInput.AlignCenter diff --git a/src/plugins/filemanager/core/dfmplugin-titlebar/qml/OptionButtonBox.qml b/src/plugins/filemanager/core/dfmplugin-titlebar/qml/OptionButtonBox.qml new file mode 100644 index 0000000000..8647e03f85 --- /dev/null +++ b/src/plugins/filemanager/core/dfmplugin-titlebar/qml/OptionButtonBox.qml @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import org.deepin.dtk +import org.dfm.base + +RowLayout { + property int iconSize: 28 + + ButtonGroup { + buttons: viewMode.children + exclusive: true + } + + Row { + id: viewMode + + ToolButton { + id: iconView + + checkable: true + + icon { + height: iconSize + name: "unchecked" + width: iconSize + } + } + + ToolButton { + id: listView + + checkable: true + + icon { + height: iconSize + name: "unchecked" + width: iconSize + } + } + + ToolButton { + id: treeView + + checkable: true + + icon { + height: iconSize + name: "unchecked" + width: iconSize + } + } + } + + ToolButton { + id: detail + + checkable: true + + Component.onCompleted: { + checked = Containment.showDetail; + } + + icon { + height: iconSize + name: "splitscreen_right" + width: iconSize + } + } +} diff --git a/src/plugins/filemanager/core/dfmplugin-titlebar/qml/Titlebar.qml b/src/plugins/filemanager/core/dfmplugin-titlebar/qml/Titlebar.qml index 72b5107357..8ca652bd2b 100644 --- a/src/plugins/filemanager/core/dfmplugin-titlebar/qml/Titlebar.qml +++ b/src/plugins/filemanager/core/dfmplugin-titlebar/qml/Titlebar.qml @@ -14,27 +14,97 @@ ContainmentItem { id: titlebar property int breadcrumbsHeight: 30 + // 是否显示侧边栏 + property bool sidebarVisible: true + // 顶栏一层的高度 + property alias topHeaderHeight: topHeader.height // 控件类型 property int widgetType: QuickUtils.Titlebar + // TODO 移动到框架事件后移除 + signal sidebarVisibleNotify(bool bVisible) + + function updateTopLeftLayout() { + Qt.callLater(sidebarVisibleNotify, sidebarVisible); + } + Layout.fillWidth: true implicitHeight: DS.Style.titleBar.height + breadcrumbsHeight + Component.onCompleted: { + updateTopLeftLayout(); + } + Window.onWindowChanged: { + if (Window.window) { + topLeftCorner.parent = Window.window.contentItem; + topLeftCorner.x = 0; + topLeftCorner.y = 0; + } + updateTopLeftLayout(); + } + onSidebarVisibleChanged: { + updateTopLeftLayout(); + } + + Row { + id: topLeftCorner + + height: DS.Style.titleBar.height + leftPadding: 5 + rightPadding: 5 + + IconLabel { + anchors.verticalCenter: parent.verticalCenter + height: 36 + width: 36 + + icon { + height: 24 + name: "dde-file-manager" + width: 24 + } + } + + ToolButton { + anchors.verticalCenter: parent.verticalCenter + height: 36 + width: 36 + + onClicked: { + if (!switchSidebar.running) { + titlebar.sidebarVisible = !titlebar.sidebarVisible; + } + } + + icon { + height: 48 + name: "window_sidebar" + width: 48 + } + } + } + ColumnLayout { anchors.fill: parent spacing: 0 RowLayout { - Layout.fillWidth: true + id: topHeader + Layout.preferredHeight: DS.Style.titleBar.height RowLayout { Layout.fillHeight: true + Layout.leftMargin: sidebarVisible ? 0 : topLeftCorner.width layoutDirection: Qt.LeftToRight - CheckBox { - id: _internal_check + Behavior on Layout.leftMargin { + NumberAnimation { + id: switchSidebar + duration: 200 + easing.type: Easing.InOutQuad + } } IconButton { @@ -46,8 +116,6 @@ ContainmentItem { } TabBar { - Layout.fillWidth: true - TabButton { text: qsTr("Home") width: implicitWidth @@ -76,10 +144,11 @@ ContainmentItem { Loader { Layout.fillWidth: true - active: Window.window + active: null !== Window.window height: DS.Style.titleBar.height sourceComponent: TitleBar { + title: "" width: parent.width } } @@ -99,19 +168,21 @@ ContainmentItem { Layout.fillWidth: true } - Rectangle { - id: tools - - Layout.fillHeight: true - color: "yellow" - width: 100 + OptionButtonBox { + height: parent.height } LineSearch { id: searchbar Layout.fillHeight: true - visible: titlebar.width > 500 + visibleState: { + return titlebar.width > 600; + } + + onVisibleStateChanged: { + Layout.preferredWidth = visibleState ? 200 : 0; + } } } } diff --git a/src/plugins/filemanager/core/dfmplugin-titlebar/titlebarcontainment.cpp b/src/plugins/filemanager/core/dfmplugin-titlebar/titlebarcontainment.cpp index 5586a1b4bd..ab02e7acac 100644 --- a/src/plugins/filemanager/core/dfmplugin-titlebar/titlebarcontainment.cpp +++ b/src/plugins/filemanager/core/dfmplugin-titlebar/titlebarcontainment.cpp @@ -10,6 +10,8 @@ #include +DFMBASE_USE_NAMESPACE + namespace dfmplugin_titlebar { TitlebarContainment::TitlebarContainment(QObject *parent) @@ -36,6 +38,36 @@ void TitlebarContainment::onUrlChanged(const QUrl &url) } } +dfmbase::Global::ViewMode TitlebarContainment::viewMode() const +{ + return internalViewMode; +} + +void TitlebarContainment::setViewMode(dfmbase::Global::ViewMode mode) +{ + if (mode != internalViewMode) { + internalViewMode = mode; + Q_EMIT viewModeChanged(); + } +} + +bool TitlebarContainment::showDetail() const +{ + return showDetailInfo; +} + +void TitlebarContainment::setShowDetail(bool b) +{ + if (b != showDetailInfo) { + showDetailInfo = b; + Q_EMIT showDetailChanged(); + + if (rootObject()) { + TitleBarEventCaller::sendDetailViewState(this, showDetailInfo); + } + } +} + void TitlebarContainment::updateController(const QUrl &url) { if (!crumbController || !crumbController->isSupportedScheme(url.scheme())) { diff --git a/src/plugins/filemanager/core/dfmplugin-titlebar/titlebarcontainment.h b/src/plugins/filemanager/core/dfmplugin-titlebar/titlebarcontainment.h index cdd2d716ad..be3b0621b7 100644 --- a/src/plugins/filemanager/core/dfmplugin-titlebar/titlebarcontainment.h +++ b/src/plugins/filemanager/core/dfmplugin-titlebar/titlebarcontainment.h @@ -5,6 +5,8 @@ #ifndef TITLEBARCONTAINMENT_H #define TITLEBARCONTAINMENT_H +#include + #include #include @@ -18,6 +20,8 @@ class TitlebarContainment : public dfmgui::Containment { Q_OBJECT Q_PROPERTY(QuickCrumbModel *crumbModel READ crumbModel CONSTANT) + Q_PROPERTY(dfmbase::Global::ViewMode viewMode READ viewMode WRITE setViewMode NOTIFY viewModeChanged FINAL) + Q_PROPERTY(bool showDetail READ showDetail WRITE setShowDetail NOTIFY showDetailChanged FINAL) public: explicit TitlebarContainment(QObject *parent = nullptr); @@ -25,13 +29,24 @@ class TitlebarContainment : public dfmgui::Containment QuickCrumbModel *crumbModel() const; Q_SLOT void onUrlChanged(const QUrl &url); + dfmbase::Global::ViewMode viewMode() const; + void setViewMode(dfmbase::Global::ViewMode mode); + Q_SIGNAL void viewModeChanged(); + + bool showDetail() const; + void setShowDetail(bool b); + Q_SIGNAL void showDetailChanged(); + private: void updateController(const QUrl &url); Q_SLOT void onHideAddrAndUpdateCrumbs(const QUrl &url); private: - CrumbInterface *crumbController = nullptr; - QuickCrumbModel *model = nullptr; + CrumbInterface *crumbController { nullptr }; + QuickCrumbModel *model { nullptr }; + + bool showDetailInfo { false }; + dfmbase::Global::ViewMode internalViewMode { dfmbase::Global::ViewMode::kIconMode }; }; }