diff --git a/examples/exhibition/ControlGroup.qml b/examples/exhibition/ControlGroup.qml new file mode 100644 index 00000000..a0b83da3 --- /dev/null +++ b/examples/exhibition/ControlGroup.qml @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +import QtQuick 2.0 +import QtQuick.Window 2.11 +import QtQuick.Layouts 1.11 +import org.deepin.dtk 1.0 +import ".." + + ControlGroup { + title: "磁盘" + ControlGroupItem { + Label { + visible: true + text: "test0111111111111" + } + Label { + visible: true + text: "test13333333333" + } + } + ControlGroupItem { + Rectangle { + width: 100 + height: 100 + color: "red" + border.color: "black" + border.width: 5 + radius: 10 + } + Switch { + checked: true + Layout.alignment: Qt.AlignHCenter + } + } + ControlGroupItem { + Rectangle { + width: 100 + height: 100 + color: "green" + border.color: "black" + border.width: 5 + radius: 10 + } + Button { + width: 100 + height: 100 + Layout.alignment: Qt.AlignHCenter + } + } + } diff --git a/examples/exhibition/qml-qt6.qrc b/examples/exhibition/qml-qt6.qrc index 8d9a8f2d..750b0636 100644 --- a/examples/exhibition/qml-qt6.qrc +++ b/examples/exhibition/qml-qt6.qrc @@ -25,5 +25,6 @@ ToolBar.qml Dialog.qml ProgressBar.qml + ControlGroup.qml diff --git a/qt6/src/dtkdeclarative_qml.qrc b/qt6/src/dtkdeclarative_qml.qrc index 2b88b5b3..fb83f85c 100644 --- a/qt6/src/dtkdeclarative_qml.qrc +++ b/qt6/src/dtkdeclarative_qml.qrc @@ -109,6 +109,8 @@ qml/ButtonIndicator.qml qml/EmbeddedProgressBar.qml qml/WaterProgressBar.qml + qml/ControlGroup.qml + qml/ControlGroupItem.qml qml/private/ProgressBarImpl.qml qml/private/ProgressBarPanel.qml qml/PlaceholderText.qml diff --git a/qt6/src/qml.cmake b/qt6/src/qml.cmake index 965e28db..998efbbf 100644 --- a/qt6/src/qml.cmake +++ b/qt6/src/qml.cmake @@ -96,6 +96,8 @@ set(QML_DTK_CONTROLS "qml/EmbeddedProgressBar.qml" "qml/WaterProgressBar.qml" "qml/PlaceholderText.qml" + "qml/ControlGroup.qml" + "qml/ControlGroupItem.qml" ) foreach(QML_FILE ${QML_DTK_CONTROLS}) diff --git a/qt6/src/qml/ArrowListView.qml b/qt6/src/qml/ArrowListView.qml index a7e53fb6..ec16f967 100644 --- a/qt6/src/qml/ArrowListView.qml +++ b/qt6/src/qml/ArrowListView.qml @@ -5,6 +5,7 @@ import QtQuick 2.11 import QtQuick.Window 2.11 import QtQuick.Layouts 1.11 +import org.deepin.dtk 1.0 as D import org.deepin.dtk.style 1.0 as DS import org.deepin.dtk.private 1.0 as P @@ -48,6 +49,20 @@ FocusScope { } interactive: Window.window ? (contentHeight > Window.window.height || model.count > maxVisibleItems) : false ScrollIndicator.vertical: ScrollIndicator { } + highlight: Rectangle { + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: 6 + anchors.rightMargin: 6 + anchors.topMargin: 6 + anchors.bottomMargin: 6 + property D.Palette backgroundColor: DS.Style.highlightPanel.background + color: D.ColorSelector.backgroundColor + radius: 6 // TODO can't display background when using dtk's InWindowBlur. + } + highlightFollowsCurrentItem: true + highlightMoveDuration: -1 + highlightMoveVelocity: 400 } P.ArrowListViewButton { diff --git a/qt6/src/qml/ButtonBox.qml b/qt6/src/qml/ButtonBox.qml index 7502dba9..2f116fc6 100644 --- a/qt6/src/qml/ButtonBox.qml +++ b/qt6/src/qml/ButtonBox.qml @@ -6,6 +6,7 @@ import QtQuick 2.11 import QtQuick.Layouts 1.11 import org.deepin.dtk 1.0 as D import org.deepin.dtk.style 1.0 as DS +import org.deepin.dtk.private 1.0 as P Control { id: control @@ -31,4 +32,27 @@ Control { outsideBorderColor: null color2: color1 } + + ParallelAnimation { + running: btnGroup.checkedButton && (btnGroup.checkedButton.x !== backgroundPanel.x || btnGroup.checkedButton.y !== backgroundPanel.y) + NumberAnimation { target: backgroundPanel; property: "x"; to: btnGroup.checkedButton.x; duration: 200 } + NumberAnimation { target: backgroundPanel; property: "y"; to: btnGroup.checkedButton.y; duration: 200 } + NumberAnimation { target: backgroundPanel; property: "width"; to: btnGroup.checkedButton.width; duration: 200 } + NumberAnimation { target: backgroundPanel; property: "height"; to: btnGroup.checkedButton.height; duration: 200 } + } + + P.ButtonPanel { + id: backgroundPanel + visible: btnGroup.checkedButton + implicitWidth: btnGroup.checkedButton.width + implicitHeight: btnGroup.checkedButton.height + button: control + outsideBorderColor: null + color1: D.Palette { + normal { + common: Qt.rgba(0, 0, 0, 0.2) + } + } + color2: color1 + } } diff --git a/qt6/src/qml/ControlGroup.qml b/qt6/src/qml/ControlGroup.qml new file mode 100644 index 00000000..d3169a52 --- /dev/null +++ b/qt6/src/qml/ControlGroup.qml @@ -0,0 +1,171 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +import QtQuick 2.0 +import QtQuick.Layouts 1.11 +import org.deepin.dtk 1.0 as D +import org.deepin.dtk.style 1.0 as DS + +ColumnLayout { + id: root + spacing: 10 + clip: true + + property string title + property bool isExpanded: true + property int interval: 400 / (itemLayout.children.length) + property int index: 0 + property int titleHeight: 36 + default property alias childItem: itemLayout.children + + Control { + id: title + Layout.fillWidth: true + Layout.preferredHeight: 36 + property int inset: 4 + leftInset: inset + rightInset: inset + topInset: inset + bottomInset: inset + state: "normal" + states: [ + State { + name: "normal" + PropertyChanges { + target: title + inset:4 + } + }, + State { + name: "hovered" + PropertyChanges { + target: title + inset:0 + } + }, + State { + name: "pressed" + PropertyChanges { + target: title + inset: 4 + } + } + ] + transitions: [ + Transition { + from: "normal" + to: "hovered" + NumberAnimation { + properties: "inset" + easing.type: Easing.Linear + duration: 500 + } + }, + Transition { + from: "hovered" + to: "normal" + NumberAnimation { + properties: "inset" + easing.type: Easing.Linear + duration: 500 + } + }, + Transition { + from: "hovered" + to: "pressed" + NumberAnimation { + properties: "inset" + easing.type: Easing.Linear + duration: 500 + } + }, + Transition { + from: "pressed" + to: "hovered" + NumberAnimation { + properties: "inset" + easing.type: Easing.Linear + duration: 500 + } + } + ] + RowLayout { + anchors.fill: parent + spacing: 0 + Label { + Layout.fillWidth: true + Layout.fillHeight: true + text: root.title + font: DTK.fontManager.t5 + verticalAlignment: Qt.AlignVCenter + } + D.DciIcon { + Layout.preferredWidth: 36 + Layout.preferredHeight: 36 + rotation: root.isExpanded ? 0 : - 90 + name: "arrow_ordinary_down" + + Behavior on rotation { + NumberAnimation { + duration: root.interval + easing.type: Easing.Linear + } + } + } + } + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: root.isExpanded = !root.isExpanded + onPressed: title.state = "pressed" + onReleased: title.state = "hovered" + onEntered: title.state = "hovered" + onExited: title.state = "normal" + } + background: Item { + Loader { + anchors.fill: parent + active: title.hovered + sourceComponent: D.RoundRectangle { + color: DS.Style.itemDelegate.normalColor + radius: DS.Style.control.radius + corners: D.RoundRectangle.TopLeftCorner | D.RoundRectangle.TopRightCorner | D.RoundRectangle.BottomLeftCorner | D.RoundRectangle.BottomRightCorner + } + } + } + } + + Timer { + id: timer + } + + ColumnLayout { + id: itemLayout + } + + onIsExpandedChanged: (isExpanded) => { + for(let i = 0; i < itemLayout.children.length; ++i) { + itemLayout.children[i].isExpanded = !itemLayout.children[i].isExpanded + } + // delay(0, timeout) + } + + function timeout() { + itemLayout.children[index].isExpanded = !itemLayout.children[index].isExpanded + ++index + if (index === itemLayout.children.length) { + timer.stop() + timer.triggered.disconnect(timeout) + index = 0 + } + } + + function delay(delayTime, cb) { + timer.interval = delayTime + timer.repeat = true + timer.triggered.connect(cb) + timer.restart() + } +} diff --git a/qt6/src/qml/ControlGroupItem.qml b/qt6/src/qml/ControlGroupItem.qml new file mode 100644 index 00000000..2a98212a --- /dev/null +++ b/qt6/src/qml/ControlGroupItem.qml @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +import QtQuick 2.0 +import QtQuick.Layouts 1.11 +import QtQml.Models 2.11 +import org.deepin.dtk 1.0 + +RowLayout { + id: root + + property int initY + property bool isExpanded: true + + y: isExpanded ? initY : - parent.parent.titleHeight + opacity: isExpanded ? 1.0 : 0.0 + visible: opacity === 0.0 ? false : true + + Layout.fillWidth: true + Layout.fillHeight: true + + Behavior on y { + NumberAnimation { + duration: parent.parent.interval + easing.type: Easing.Linear + } + } + Behavior on opacity { + NumberAnimation { + duration: parent.parent.interval + easing.type: Easing.Linear + } + } + + Component.onCompleted: { + root.initY = root.y + } +} diff --git a/qt6/src/qml/FloatingMessage.qml b/qt6/src/qml/FloatingMessage.qml index 5b1d9cb9..65c90a9f 100644 --- a/qt6/src/qml/FloatingMessage.qml +++ b/qt6/src/qml/FloatingMessage.qml @@ -23,9 +23,18 @@ D.FloatingMessageContainer { width: DS.Style.floatingMessage.closeButtonSize height: DS.Style.floatingMessage.closeButtonSize } - onClicked: D.DTK.closeMessage(control) + onClicked: { + floatingPanel.state = "small" + destroyTimer.running = true + } + Timer { + id: destroyTimer + interval: 1200; running: false; repeat: true + onTriggered: D.DTK.closeMessage(control) + } } + duration: 4000 panel: FloatingPanel { id: floatingPanel @@ -34,12 +43,24 @@ D.FloatingMessageContainer { rightPadding: 10 topPadding: 0 bottomPadding: 0 - + opacity: 0.0 + state: "small" + Timer { + id: timer + interval: 100; running: false; repeat: false + onTriggered: { + floatingPanel.y = floatingPanel.parent.height + floatingPanel.state = "normal" + console.log("onCompleted", floatingPanel.y, floatingPanel.parent.width, floatingPanel.parent.height) + } + } + Component.onCompleted: { + timer.running = true + } contentItem: RowLayout { height: DS.Style.floatingMessage.minimumHeight width: Math.min(DS.Style.floatingMessage.maximumWidth, children.width + floatingPanel.leftPadding - floatingPanel.rightPadding) spacing: 10 - Loader { id: iconLoader Layout.alignment: Qt.AlignVCenter @@ -77,6 +98,46 @@ D.FloatingMessageContainer { visible: active sourceComponent: button } + + ParallelAnimation { + running: closeButton.item.hovered + NumberAnimation { target: closeButton; property: "scale"; to: 1.25; duration: 500 } + NumberAnimation { target: closeButton; property: "rotation"; to: 90; duration: 500 } + } + ParallelAnimation { + running: !closeButton.item.hovered + NumberAnimation { target: closeButton; property: "scale"; to: 1; duration: 500 } + NumberAnimation { target: closeButton; property: "rotation"; to: 0; duration: 500 } + } } + + states: [ + State { + name: "normal" + PropertyChanges { + target: floatingPanel + y: 0 + opacity: 1.0 + scale: 1.0 + } + }, + State { + name: "small" + PropertyChanges { + target: floatingPanel + y: floatingPanel.parent.height + opacity: 0.0 + scale: 0.2 + } + } + ] + + transitions: Transition { + NumberAnimation { + properties: "y, opacity, scale" + easing.type: Easing.Linear + duration: 1000 + } + } } } diff --git a/qt6/src/qml/Menu.qml b/qt6/src/qml/Menu.qml index 5c43d4d6..46038991 100644 --- a/qt6/src/qml/Menu.qml +++ b/qt6/src/qml/Menu.qml @@ -42,13 +42,14 @@ T.Menu { } contentItem: Control { - topPadding: 10 // TODO how to clip radius + topPadding: 6 // TODO how to clip radius bottomPadding: topPadding leftPadding: 0 rightPadding: leftPadding contentItem: ColumnLayout { id: viewLayout + spacing: 0 Loader { Layout.fillWidth: true diff --git a/qt6/src/qml/MenuItem.qml b/qt6/src/qml/MenuItem.qml index 0107be5a..8e22b1e6 100644 --- a/qt6/src/qml/MenuItem.qml +++ b/qt6/src/qml/MenuItem.qml @@ -102,15 +102,5 @@ T.MenuItem { radius: DS.Style.control.radius } } - - Loader { - anchors.fill: parent - active: control.down || control.highlighted - sourceComponent: Rectangle { - property D.Palette backgroundColor: DS.Style.highlightPanel.background - color: D.ColorSelector.backgroundColor - radius: 1 // TODO can't display background when using dtk's InWindowBlur. - } - } } } diff --git a/qt6/src/qml/Switch.qml b/qt6/src/qml/Switch.qml index 4f148062..cbc62c37 100644 --- a/qt6/src/qml/Switch.qml +++ b/qt6/src/qml/Switch.qml @@ -20,43 +20,57 @@ T.Switch { topPadding: DS.Style.control.vPadding bottomPadding: DS.Style.control.vPadding spacing: DS.Style.control.spacing + D.ColorSelector.hovered: false // disable hover ==> normal animation - indicator: Rectangle { + indicator: D.DciIcon { + id: handle implicitWidth: DS.Style.switchButton.indicatorWidth implicitHeight: DS.Style.switchButton.indicatorHeight x: text ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) : control.leftPadding + (control.availableWidth - width) / 2 y: control.topPadding + (control.availableHeight - height) / 2 - radius: DS.Style.control.radius - color: control.D.ColorSelector.backgroundColor - opacity: control.D.ColorSelector.controlState === D.DTK.DisabledState ? 0.4 : 1 - D.DciIcon { - id: handle - x: Math.max(0, Math.min(parent.width - width, control.visualPosition * parent.width - (width / 2))) - y: (parent.height - height) / 2 - width: DS.Style.switchButton.handleWidth - height: DS.Style.switchButton.handleHeight - sourceSize: Qt.size(DS.Style.switchButton.handleWidth, DS.Style.switchButton.handleHeight) - name: DS.Style.switchButton.iconName - opacity: control.D.ColorSelector.controlState === D.DTK.DisabledState && control.checked ? 0.4 : 1 - palette { - highlight: control.checked ? control.palette.highlight : control.D.ColorSelector.handleColor - highlightForeground: control.palette.highlightedText - foreground: control.palette.windowText - background: control.palette.window - } - mode: control.D.ColorSelector.controlState - theme: control.D.ColorSelector.controlTheme - fallbackToQIcon: false + width: DS.Style.switchButton.handleWidth + height: DS.Style.switchButton.handleHeight + sourceSize: Qt.size(DS.Style.switchButton.indicatorWidth, DS.Style.switchButton.indicatorWidth) + name: control.checked ? "switch_on_static" : "switch_off_static" + opacity: control.D.ColorSelector.controlState === D.DTK.DisabledState && control.checked ? 0.4 : 1 + palette { + highlight: control.checked ? control.palette.highlight : control.D.ColorSelector.handleColor + highlightForeground: control.palette.highlightedText + foreground: control.palette.windowText + background: control.palette.window + } + mode: control.D.ColorSelector.controlState + theme: control.D.ColorSelector.controlTheme + fallbackToQIcon: false + } - Behavior on x { - enabled: !control.down - SmoothedAnimation { velocity: 200 } - } + Timer { + id: toggletimer + interval: 200 + onTriggered: { + control.toggle() + handle.name = control.checked ? "switch_on_static" : "switch_off_static" } } + function palyAndSetImage() { + handle.name = !control.checked ? "switch_on" : "switch_off" + handle.play(D.DTK.NormalState) + toggletimer.start(); + } + + Keys.onSpacePressed: palyAndSetImage() + Keys.onEnterPressed: palyAndSetImage() + Keys.onReturnPressed: palyAndSetImage() + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton + onClicked: palyAndSetImage() + } + contentItem: Label { leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0 rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0 diff --git a/qt6/src/qml/ToolButton.qml b/qt6/src/qml/ToolButton.qml index 5fc097c3..1e07efd5 100644 --- a/qt6/src/qml/ToolButton.qml +++ b/qt6/src/qml/ToolButton.qml @@ -10,7 +10,7 @@ import org.deepin.dtk.private 1.0 as P T.ToolButton { id: control - property D.Palette textColor: checked ? DS.Style.checkedButton.text : (highlighted ? DS.Style.highlightedButton.text : DS.Style.button.text) + property D.Palette textColor: checked ? DS.Style.highlightedButton.text : (highlighted ? DS.Style.highlightedButton.text : DS.Style.button.text) implicitWidth: DS.Style.control.implicitWidth(control) implicitHeight: DS.Style.control.implicitHeight(control) @@ -66,10 +66,46 @@ T.ToolButton { } } + states: [ + State { + name: "hovered" + when: control.hovered && !control.checked + PropertyChanges { + target: contentItem + scale : 1.2 + } + PropertyChanges { + target: background + scale : 1.0 + } + + }, + State { + name: "checked" + when: control.checked + PropertyChanges { + target: contentItem + scale : 1.0 + } + } + ] + + transitions: Transition { + NumberAnimation { properties: "scale"; easing.type: Easing.InOutQuad } + } + background: P.ButtonPanel { + visible: control.state === "hovered" + scale : 0.9 implicitWidth: DS.Style.toolButton.width implicitHeight: DS.Style.toolButton.height button: control outsideBorderColor: null + color1: D.Palette { + normal { + common: Qt.rgba(0, 0, 0, 0.1) + } + } + color2 : color1 } } diff --git a/src/dtkdeclarative_assets.qrc b/src/dtkdeclarative_assets.qrc index c4126298..e4d3ec91 100644 --- a/src/dtkdeclarative_assets.qrc +++ b/src/dtkdeclarative_assets.qrc @@ -46,7 +46,10 @@ icons/bloom/slider_point_left.dci icons/bloom/slider_round_hor.dci icons/bloom/slider_round_ver.dci - icons/bloom/switch_button.dci + icons/bloom/switch_on.dci + icons/bloom/switch_off.dci + icons/bloom/switch_on_static.dci + icons/bloom/switch_off_static.dci icons/bloom/slider_point_up.dci icons/bloom/checkbox_mix.dci icons/bloom/checkbox_checked.dci diff --git a/src/icons/bloom/checkbox_checked.dci b/src/icons/bloom/checkbox_checked.dci index bae6718e..de394945 100644 Binary files a/src/icons/bloom/checkbox_checked.dci and b/src/icons/bloom/checkbox_checked.dci differ diff --git a/src/icons/bloom/checkbox_unchecked.dci b/src/icons/bloom/checkbox_unchecked.dci index e68eed24..413e4145 100644 Binary files a/src/icons/bloom/checkbox_unchecked.dci and b/src/icons/bloom/checkbox_unchecked.dci differ diff --git a/src/icons/bloom/radio_checked.dci b/src/icons/bloom/radio_checked.dci index 1075ebd3..69df89f3 100644 Binary files a/src/icons/bloom/radio_checked.dci and b/src/icons/bloom/radio_checked.dci differ diff --git a/src/icons/bloom/radio_unchecked.dci b/src/icons/bloom/radio_unchecked.dci index 641029d6..792ce8ec 100644 Binary files a/src/icons/bloom/radio_unchecked.dci and b/src/icons/bloom/radio_unchecked.dci differ diff --git a/src/icons/bloom/switch_off.dci b/src/icons/bloom/switch_off.dci new file mode 100644 index 00000000..957f499a Binary files /dev/null and b/src/icons/bloom/switch_off.dci differ diff --git a/src/icons/bloom/switch_off_static.dci b/src/icons/bloom/switch_off_static.dci new file mode 100644 index 00000000..ffdc6758 Binary files /dev/null and b/src/icons/bloom/switch_off_static.dci differ diff --git a/src/icons/bloom/switch_on.dci b/src/icons/bloom/switch_on.dci new file mode 100644 index 00000000..20ec32c8 Binary files /dev/null and b/src/icons/bloom/switch_on.dci differ diff --git a/src/icons/bloom/switch_on_static.dci b/src/icons/bloom/switch_on_static.dci new file mode 100644 index 00000000..6e82fc0a Binary files /dev/null and b/src/icons/bloom/switch_on_static.dci differ diff --git a/src/private/dmessagemanager.cpp b/src/private/dmessagemanager.cpp index 4d1bd33d..0941a416 100644 --- a/src/private/dmessagemanager.cpp +++ b/src/private/dmessagemanager.cpp @@ -188,11 +188,15 @@ void MessageManager::ensureLayout() QQmlComponent columnCom(qmlEngine(parent())); columnCom.setData("import QtQuick 2.11\n" "Column {\n" + " spacing: 0\n" " anchors {\n" " bottom: parent.bottom\n" - " bottomMargin: 10;\n" + " bottomMargin: 20;\n" " horizontalCenter: parent.horizontalCenter\n" " }\n" + " Component.onCompleted: {\n" + " console.log(\"parent onCompleted\", children.length);\n" + " }" "}\n", QUrl()); auto layout = columnCom.beginCreate(qmlContext(parent())); setLayout(qobject_cast(layout)); diff --git a/src/private/dquickdciiconimage.cpp b/src/private/dquickdciiconimage.cpp index cffac58a..3bf4d794 100644 --- a/src/private/dquickdciiconimage.cpp +++ b/src/private/dquickdciiconimage.cpp @@ -18,6 +18,47 @@ static QString appIconThemeName() return DGuiApplicationHelper::instance()->applicationTheme()->iconThemeName(); } +static QString findDciIconPath(const QString &iconName, const QString &themeName) +{ + QString iconPath; + auto cached = DIconTheme::cached(); + + if (cached) { + iconPath = cached->findDciIconFile(iconName, themeName); + } else { + iconPath = DIconTheme::findDciIconFile(iconName, themeName); + } + return iconPath; +} + +static DDciIcon::Mode controlState2DciMode(int state) +{ + DDciIcon::Mode dcimode = DDciIcon::Normal; + switch (state) { + case DQMLGlobalObject::NormalState: + dcimode = DDciIcon::Normal; + break; + case DQMLGlobalObject::DisabledState: + dcimode = DDciIcon::Disabled; + break; + case DQMLGlobalObject::HoveredState: + dcimode = DDciIcon::Hover; + break; + case DQMLGlobalObject::PressedState: + dcimode = DDciIcon::Pressed; + break; + default: + break; + } + + return dcimode; +} + +static inline DDciIcon::Theme dciTheme(DGuiApplicationHelper::ColorType type) +{ + return type == DGuiApplicationHelper::DarkType ? DDciIcon::Dark : DDciIcon::Light; +} + DQuickDciIconImageItemPrivate::DQuickDciIconImageItemPrivate(DQuickDciIconImagePrivate *pqq) : parentPriv(pqq) { @@ -31,11 +72,27 @@ void DQuickDciIconImageItemPrivate::maybeUpdateUrl() return DQuickIconImagePrivate::maybeUpdateUrl(); } - QUrl url; - url.setScheme(QLatin1String("image")); - url.setHost(QLatin1String("dtk.dci.icon")); - url.setQuery(getUrlQuery()); - q->setSource(url); + QString iconPath = findDciIconPath(parentPriv->imageItem->name(), appIconThemeName()); + if (iconPath.isEmpty()) + return DQuickIconImagePrivate::maybeUpdateUrl(); + + updatePlayer(); + + if (player) + player->setMode(controlState2DciMode(parentPriv->mode)); +} + +void DQuickDciIconImageItemPrivate::play(int mode) +{ + Q_Q(DQuickIconImage); + if (parentPriv->imageItem->name().isEmpty() || iconType != ThemeIconName) { + return; + } + + updatePlayer(); + + if (player) + player->play(controlState2DciMode(mode)); } QUrlQuery DQuickDciIconImageItemPrivate::getUrlQuery() @@ -56,6 +113,53 @@ QUrlQuery DQuickDciIconImageItemPrivate::getUrlQuery() return query; } +void DQuickDciIconImageItemPrivate::updatePlayerIconSize() +{ + if (!player) + return; + + int boundingSize = qMax(q_func()->sourceSize().width(), q_func()->sourceSize().height()); + + player->setIconSize(boundingSize); +} + +void DQuickDciIconImageItemPrivate::updatePlayer() +{ + if (!player) { + Q_Q(DQuickIconImage); + player = new DDciIconPlayer(parentPriv->imageItem); + QObject::connect(player, &DDciIconPlayer::updated, parentPriv->imageItem, [this](){ + parentPriv->imageItem->setImage(player->currentImage()); + }); + QObject::connect(parentPriv->imageItem, &DQuickIconImage::sourceSizeChanged, player, [this](){ + updatePlayerIconSize(); + }); + } + + QString iconPath = findDciIconPath(parentPriv->imageItem->name(), appIconThemeName()); + + // 防止频繁构造 dciicon + if (iconPathCache != iconPath) { + DDciIcon dciIcon(iconPath); + if (!dciIcon.isNull()) { + player->setIcon(dciIcon); + iconPathCache = iconPath; + } + } + + player->setTheme(dciTheme(parentPriv->theme)); + + DDciIconPalette palette = parentPriv->palette; + if (!parentPriv->palette.foreground().isValid() && q_func()->color().isValid()) + palette.setForeground(q_func()->color()); + + player->setPalette(palette); + + updatePlayerIconSize(); + + player->setDevicePixelRatio(devicePixelRatio); +} + DQuickDciIconImagePrivate::DQuickDciIconImagePrivate(DQuickDciIconImage *qq) : DObjectPrivate(qq) , imageItem(new DQuickIconImage(*new DQuickDciIconImageItemPrivate(this), qq)) @@ -76,6 +180,11 @@ void DQuickDciIconImagePrivate::updateImageSourceUrl() imageItem->d_func()->maybeUpdateUrl(); } +void DQuickDciIconImagePrivate::play(DQMLGlobalObject::ControlState mode) +{ + imageItem->d_func()->play(mode); +} + DQuickDciIconImage::DQuickDciIconImage(QQuickItem *parent) : QQuickItem(parent) , DObject(*new DQuickDciIconImagePrivate(this)) @@ -119,6 +228,13 @@ void DQuickDciIconImage::setMode(DQMLGlobalObject::ControlState mode) Q_EMIT modeChanged(); } +void DQuickDciIconImage::play(DQMLGlobalObject::ControlState mode) +{ + D_D(DQuickDciIconImage); + if (d->imageItem) + d->play(mode); +} + DGuiApplicationHelper::ColorType DQuickDciIconImage::theme() const { D_DC(DQuickDciIconImage); @@ -226,15 +342,7 @@ Dtk::Quick::DQuickIconImage *DQuickDciIconImage::imageItem() const bool DQuickDciIconImage::isNull(const QString &iconName) { - QString iconPath; - auto cached = DIconTheme::cached(); - - if (cached) { - iconPath = cached->findDciIconFile(iconName, appIconThemeName()); - } else { - iconPath = DIconTheme::findDciIconFile(iconName, appIconThemeName()); - } - return iconPath.isEmpty(); + return findDciIconPath(iconName, appIconThemeName()).isEmpty(); } DQuickIconAttached *DQuickDciIconImage::qmlAttachedProperties(QObject *object) diff --git a/src/private/dquickdciiconimage_p.h b/src/private/dquickdciiconimage_p.h index 8b7fb3d5..1d61f0f2 100644 --- a/src/private/dquickdciiconimage_p.h +++ b/src/private/dquickdciiconimage_p.h @@ -46,6 +46,7 @@ class DQuickDciIconImage : public QQuickItem, DCORE_NAMESPACE::DObject DQMLGlobalObject::ControlState mode() const; void setMode(DQMLGlobalObject::ControlState mode); + Q_INVOKABLE void play(DQMLGlobalObject::ControlState mode); DGuiApplicationHelper::ColorType theme() const; void setTheme(DGuiApplicationHelper::ColorType theme); diff --git a/src/private/dquickdciiconimage_p_p.h b/src/private/dquickdciiconimage_p_p.h index 81fd039b..5d2ea787 100644 --- a/src/private/dquickdciiconimage_p_p.h +++ b/src/private/dquickdciiconimage_p_p.h @@ -12,6 +12,7 @@ #include #include +#include DQUICK_BEGIN_NAMESPACE class DQuickDciIconImageItemPrivate; @@ -22,10 +23,15 @@ class DQuickDciIconImageItemPrivate : public DQuickIconImagePrivate public: DQuickDciIconImageItemPrivate(DQuickDciIconImagePrivate *pqq); void maybeUpdateUrl(); + void play(int mode); QUrlQuery getUrlQuery(); + void updatePlayer(); + void updatePlayerIconSize(); private: DQuickDciIconImagePrivate *parentPriv; + DDciIconPlayer *player = nullptr; + QString iconPathCache; }; class DQuickDciIconImagePrivate : public DCORE_NAMESPACE::DObjectPrivate @@ -36,6 +42,7 @@ class DQuickDciIconImagePrivate : public DCORE_NAMESPACE::DObjectPrivate DQuickDciIconImagePrivate(DQuickDciIconImage *qq); void layout(); void updateImageSourceUrl(); + void play(DQMLGlobalObject::ControlState mode); DDciIconPalette palette; DQuickIconImage *imageItem; diff --git a/src/private/dquickiconimage.cpp b/src/private/dquickiconimage.cpp index 1772751a..a2f19c11 100644 --- a/src/private/dquickiconimage.cpp +++ b/src/private/dquickiconimage.cpp @@ -108,6 +108,11 @@ void DQuickIconImagePrivate::maybeUpdateUrl() q->setSource(url); } +void DQuickIconImagePrivate::play(int mode) +{ + Q_UNUSED(mode) +} + QUrlQuery DQuickIconImagePrivate::getUrlQuery() { QUrlQuery query; @@ -316,6 +321,12 @@ void DQuickIconImage::setFallbackSource(const QUrl &newSource) d->maybeUpdateUrl(); } +void DQuickIconImage::setImage(const QImage &img) +{ + D_D(DQuickIconImage); + d->setImage(img); +} + DQuickIconImage::DQuickIconImage(DQuickIconImagePrivate &dd, QQuickItem *parent) : QQuickImage(dd, parent) { diff --git a/src/private/dquickiconimage_p.h b/src/private/dquickiconimage_p.h index 3afe3260..58fdb98b 100644 --- a/src/private/dquickiconimage_p.h +++ b/src/private/dquickiconimage_p.h @@ -65,6 +65,7 @@ public Q_SLOTS: void setMode(Mode mode); void setColor(const QColor &color); void setFallbackSource(const QUrl &newSource); + void setImage(const QImage &img); Q_SIGNALS: void nameChanged(); diff --git a/src/private/dquickiconimage_p_p.h b/src/private/dquickiconimage_p_p.h index e6973c53..847775af 100644 --- a/src/private/dquickiconimage_p_p.h +++ b/src/private/dquickiconimage_p_p.h @@ -23,6 +23,7 @@ class DQuickIconImagePrivate : public QQuickImagePrivate public: void init(); virtual void maybeUpdateUrl(); + virtual void play(int mode); QUrlQuery getUrlQuery(); DQuickIconImage::Mode getIconMode() const;