diff --git a/CHANGELOG.md b/CHANGELOG.md index fd1e7b180..4c2361af0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,33 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [6.7.51] - 2026-10-15 + +### Changed + +- I18n: Translate dtkdeclarative.ts in mn (#720) + +### Fixed + +- Size popup before it is mapped + +## [6.7.50] - 2026-09-22 + +### Added + +- Add split screen menu to title bar + +### Changed + +- I18n: Translate dtkdeclarative.ts in sv (#674) + +### Fixed + +- Set fixed default implicit size for Window popupType +- Restore implicit size bindings on Popup for qt6 +- Correct model reference in ArrowListView +- Use formal parameter in ThemeMenu signal handler + ## [6.7.49] - 2026-09-10 ### Added diff --git a/VERSION b/VERSION index c9952ecc6..2b097dadd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.7.49 +6.7.51 diff --git a/archlinux/PKGBUILD b/archlinux/PKGBUILD index a1a4c87ef..80299cf5f 100644 --- a/archlinux/PKGBUILD +++ b/archlinux/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: zccrs pkgname=dtkdeclarative-git -pkgver=6.7.49 +pkgver=6.7.51 pkgrel=1 sourcename=dtkdeclarative sourcetars=("$sourcename"_"$pkgver".tar.xz) diff --git a/debian/changelog b/debian/changelog index b4b9646b6..f9fc34cab 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,15 @@ +dtkdeclarative (6.7.51) unstable; urgency=medium + + * Release 6.7.51 + + -- yeshanshan Thu, 15 Oct 2026 20:23:34 +0800 + +dtkdeclarative (6.7.50) unstable; urgency=medium + + * Release 6.7.50 + + -- yeshanshan Tue, 22 Sep 2026 16:19:42 +0800 + dtkdeclarative (6.7.49) unstable; urgency=medium * Release 6.7.49 diff --git a/qt6/src/qml/ArrowListView.qml b/qt6/src/qml/ArrowListView.qml index f6b2eace5..eb7c880c2 100644 --- a/qt6/src/qml/ArrowListView.qml +++ b/qt6/src/qml/ArrowListView.qml @@ -15,6 +15,17 @@ FocusScope { property int itemHeight: DS.Style.arrowListView.itemHeight property alias view: itemsView + // Full height of the list, to size a popup before it is mapped. + readonly property real sizedHeight: { + if (itemsView.count === 0) + return 0 + const item = itemsView.itemAtIndex(0) + const measured = itemsView.contentHeight > 0 && item ? (item.height || item.implicitHeight) : 0 + const arrowStrips = itemsView.count > maxVisibleItems + ? 2 * (DS.Style.arrowListView.stepButtonIconSize.height + contentLayout.spacing) : 0 + return Math.min(itemsView.count, maxVisibleItems) * (measured > 0 ? measured : itemHeight) + arrowStrips + } + implicitWidth: Math.max(DS.Style.arrowListView.width, contentLayout.implicitWidth) implicitHeight: contentLayout.implicitHeight @@ -25,7 +36,7 @@ FocusScope { visible: itemsView.interactive Layout.alignment: Qt.AlignHCenter Layout.fillWidth: true - Layout.preferredHeight: implicitHeight + Layout.preferredHeight: DS.Style.arrowListView.stepButtonIconSize.height view: itemsView stepSize: control.itemHeight direction: P.ArrowListViewButton.UpButton @@ -36,6 +47,7 @@ FocusScope { clip: true Layout.fillWidth: true Layout.fillHeight: true + onHeightChanged: if (height > 0 && contentHeight === 0) forceLayout() implicitHeight: itemsView.count > 0 ? Math.min(contentHeight, maxVisibleItems * contentHeight / itemsView.count) : 0 implicitWidth:{ var maxWidth = DS.Style.arrowListView.width @@ -81,7 +93,7 @@ FocusScope { visible: itemsView.interactive Layout.alignment: Qt.AlignHCenter Layout.fillWidth: true - Layout.preferredHeight: implicitHeight + Layout.preferredHeight: DS.Style.arrowListView.stepButtonIconSize.height view: itemsView stepSize: control.itemHeight direction: P.ArrowListViewButton.DownButton diff --git a/qt6/src/qml/ComboBox.qml b/qt6/src/qml/ComboBox.qml index bd8983984..b63ff6855 100644 --- a/qt6/src/qml/ComboBox.qml +++ b/qt6/src/qml/ComboBox.qml @@ -194,6 +194,9 @@ T.ComboBox { rightMargin: DS.Style.popup.margin palette: control.palette implicitWidth: control.flat ? Math.max(contentItem.implicitWidth, control.width) : control.width + // 60 is a temporary viewport so the list can realize one row and be measured. + implicitHeight: (contentItem.sizedHeight > 0 ? contentItem.sizedHeight : 2 * DS.Style.arrowListView.itemHeight) + + topPadding + bottomPadding onClosed: control.isInteractingWithContent = false Connections { target: control diff --git a/qt6/src/qml/PasswordEdit.qml b/qt6/src/qml/PasswordEdit.qml index a58573754..990d48371 100644 --- a/qt6/src/qml/PasswordEdit.qml +++ b/qt6/src/qml/PasswordEdit.qml @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2020 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later diff --git a/qt6/src/qml/ThemeMenu.qml b/qt6/src/qml/ThemeMenu.qml index 3197bf069..4a2cfe62a 100644 --- a/qt6/src/qml/ThemeMenu.qml +++ b/qt6/src/qml/ThemeMenu.qml @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2021 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -14,7 +14,7 @@ Menu { ActionGroup { id: themeEG exclusive: true - onTriggered: { + onTriggered: function(action) { D.ApplicationHelper.setPaletteType(action.themeType) } } diff --git a/qt6/src/qml/TitleBar.qml b/qt6/src/qml/TitleBar.qml index 48902a9d5..68bbf07b4 100644 --- a/qt6/src/qml/TitleBar.qml +++ b/qt6/src/qml/TitleBar.qml @@ -28,6 +28,7 @@ Item { // visibility access property bool fullScreenButtonVisible: true + property bool splitScreenEnabled: true signal toggleWindowState() property alias windowButtonGroup: windowButtonsLoader.sourceComponent @@ -179,6 +180,7 @@ Item { embedMode: control.embedMode textColor: control.textColor fullScreenButtonVisible: control.fullScreenButtonVisible + splitScreenEnabled: control.splitScreenEnabled Component.onCompleted: { control.toggleWindowState.connect(maxOrWinded) } diff --git a/qt6/src/qml/WindowButtonGroup.qml b/qt6/src/qml/WindowButtonGroup.qml index a86a9de13..8d1cdd346 100644 --- a/qt6/src/qml/WindowButtonGroup.qml +++ b/qt6/src/qml/WindowButtonGroup.qml @@ -14,6 +14,7 @@ RowLayout { property D.Palette textColor: DS.Style.button.text property bool fullScreenButtonVisible: true + property bool splitScreenEnabled: true property bool embedMode: false signal maxOrWinded() @@ -76,6 +77,7 @@ RowLayout { WindowButton { id: maxOrWindedBtn; objectName: "maxOrWindedBtn" + hoverEnabled: true property bool hasWindowFlag/*: (Window.window.flags & Qt.WindowMaximizeButtonHint)*/ Component.onCompleted: hasWindowFlag = (Window.window.flags & Qt.WindowMaximizeButtonHint) @@ -86,7 +88,82 @@ RowLayout { property bool isMaximized: Window.window.visibility === Window.Maximized icon.name: isMaximized ? "window_restore" : "window_maximize" textColor: control.textColor - onClicked: maxOrWinded() + property bool __longPressed: false + property bool __menuRequested: false + + function showSplitMenu() { + if (!control.splitScreenEnabled || !visible || !enabled) + return false + if (!__menuRequested) + __menuRequested = control.__dwindow.showSplitMenu(maxOrWindedBtn) + return __menuRequested + } + + function hideSplitMenu(delay) { + hoverTimer.stop() + holdTimer.stop() + control.__dwindow.hideSplitMenu(delay) + __menuRequested = false + } + + onHoveredChanged: { + if (hovered && !pressed) + hoverTimer.restart() + else if (!hovered) { + hoverTimer.stop() + if (pressed) + holdTimer.stop() + else + hideSplitMenu(true) + } + } + onPressed: { + __longPressed = false + hoverTimer.stop() + holdTimer.restart() + } + onReleased: { + holdTimer.stop() + if (!__longPressed) + hideSplitMenu(false) + else + __menuRequested = false + } + onCanceled: { + hideSplitMenu(true) + __longPressed = false + } + onClicked: { + if (!__longPressed) + control.maxOrWinded() + __longPressed = false + } + onVisibleChanged: { + if (!visible) { + hoverTimer.stop() + holdTimer.stop() + __menuRequested = false + } + } + onEnabledChanged: if (!enabled) hideSplitMenu(false) + + Timer { + id: hoverTimer + interval: 700 + onTriggered: maxOrWindedBtn.showSplitMenu() + } + Timer { + id: holdTimer + interval: 300 + onTriggered: maxOrWindedBtn.__longPressed = maxOrWindedBtn.showSplitMenu() + } + Connections { + target: control + function onSplitScreenEnabledChanged() { + if (!control.splitScreenEnabled) + maxOrWindedBtn.hideSplitMenu(false) + } + } visible: (hasWindowFlag && !__forceHind && __canResize && __sizeResizable) enabled: (__canMaximize && __canResize) diff --git a/src/dquickwindow.cpp b/src/dquickwindow.cpp index 4988bc1dd..8b5b863bc 100644 --- a/src/dquickwindow.cpp +++ b/src/dquickwindow.cpp @@ -15,6 +15,8 @@ #include #include +#include +#include DQUICK_BEGIN_NAMESPACE @@ -1010,6 +1012,45 @@ void DQuickWindowAttached::popupSystemWindowMenu() DWindowManagerHelper::popupSystemWindowMenu(window()); } +// Platform split-menu functions take a Qt window id and a global logical rectangle. +bool DQuickWindowAttached::showSplitMenu(QQuickItem *button) +{ + D_D(DQuickWindowAttached); + auto *w = window(); + if (!w || !w->isVisible() || !w->handle() || !button || button->window() != w + || !button->isVisible() || !button->isEnabled()) + return false; + + const auto support = reinterpret_cast( + QGuiApplication::platformFunction("_d_supportSplitMenu")); + const auto show = reinterpret_cast( + QGuiApplication::platformFunction("_d_showSplitMenu")); + const auto hide = QGuiApplication::platformFunction("_d_hideSplitMenu"); + const WId wid = w->winId(); + if (!support || !show || !hide || !support(wid)) + return false; + + const QRect rect = button->mapRectToScene(button->boundingRect()).toAlignedRect(); + if (!rect.isValid()) + return false; + show(wid, QRect(w->mapToGlobal(rect.topLeft()), rect.size())); + d->splitMenuWindowId = wid; + return true; +} + +void DQuickWindowAttached::hideSplitMenu(bool delay) +{ + D_D(DQuickWindowAttached); + if (!d->splitMenuWindowId) + return; + const auto hide = reinterpret_cast( + QGuiApplication::platformFunction("_d_hideSplitMenu")); + if (hide) + hide(d->splitMenuWindowId, delay); + if (!delay) + d->splitMenuWindowId = 0; +} + bool DQuickWindowAttached::setWindowBlurAreaByWM(const QVector &area) { D_D(DQuickWindowAttached); diff --git a/src/dquickwindow.h b/src/dquickwindow.h index db67525b4..bf74fad1f 100644 --- a/src/dquickwindow.h +++ b/src/dquickwindow.h @@ -148,6 +148,9 @@ public Q_SLOTS: void popupSystemWindowMenu(); + bool showSplitMenu(QQuickItem *button); + void hideSplitMenu(bool delay = true); + bool setWindowBlurAreaByWM(const QVector &area); bool setWindowBlurAreaByWM(const QList &area); diff --git a/src/private/dquickwindow_p.h b/src/private/dquickwindow_p.h index 801279e78..07efd715e 100644 --- a/src/private/dquickwindow_p.h +++ b/src/private/dquickwindow_p.h @@ -56,6 +56,7 @@ class DQuickWindowAttachedPrivate : public DTK_CORE_NAMESPACE::DObjectPrivate #endif QPointer window = nullptr; + WId splitMenuWindowId = 0; DPlatformHandle *handle = nullptr; BoolOptional explicitEnable {Invalid}; BoolOptional explicitTranslucentBackground {Invalid}; diff --git a/src/translations/dtkdeclarative_mn.ts b/src/translations/dtkdeclarative_mn.ts new file mode 100644 index 000000000..de18cf8cb --- /dev/null +++ b/src/translations/dtkdeclarative_mn.ts @@ -0,0 +1,131 @@ + + + AboutAction + + + About + Тухай + + + + AboutDialog + + + Version + Хувилбар + + + + Homepage + Нүүр хуудас + + + + Description + Тайлбар + + + + Acknowledgements + Талархал + + + + Sincerely appreciate the %1 used. + Ашигласан %1-д үнэхээр талархаж байна. + + + + open-source software + нээлттэй эхийн програм хангамж + + + + HelpAction + + + Help + Тусламж + + + + LicenseDialog + + + open-source software + нээлттэй эхийн програм хангамж + + + + QuitAction + + + Exit + Гарах + + + + SearchEdit + + + Search + Хайх + + + + TextField + + + Copy + Хуулах + + + + Cut + Таслах + + + + Paste + Буулгах + + + + Select All + Бүгдийг сонгох + + + + Undo + Буцаах + + + + Redo + Дахин хийх + + + + ThemeMenu + + + Theme + Загвар + + + + Light Theme + Цайвар загвар + + + + Dark Theme + Бараан загвар + + + + System Theme + Системийн загвар + + + \ No newline at end of file