diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3975896e..dbbe397c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,100 +1,101 @@ project(kirigami) if (NOT STATIC_LIBRARY) ecm_create_qm_loader(kirigami_QM_LOADER libkirigami2plugin_qt) else() set(KIRIGAMI_STATIC_FILES libkirigami/basictheme.cpp libkirigami/platformtheme.cpp libkirigami/kirigamipluginfactory.cpp) endif() include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libkirigami ${CMAKE_CURRENT_BINARY_DIR}/libkirigami) set(kirigami_SRCS kirigamiplugin.cpp enums.cpp delegaterecycler.cpp desktopicon.cpp settings.cpp formlayoutattached.cpp + scenepositionattached.cpp mnemonicattached.cpp ${kirigami_QM_LOADER} ${KIRIGAMI_STATIC_FILES} ) add_subdirectory(libkirigami) if(STATIC_LIBRARY) # When using the static library, all QML files need to be shipped within the # .a file. qt5_add_resources(RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/../kirigami.qrc) endif(STATIC_LIBRARY) add_library(kirigamiplugin ${kirigami_SRCS} ${RESOURCES}) if(STATIC_LIBRARY) SET_TARGET_PROPERTIES(kirigamiplugin PROPERTIES AUTOMOC_MOC_OPTIONS -Muri=org.kde.kirigami) if (UNIX AND NOT CMAKE_SYSTEM_NAME STREQUAL "Android" AND NOT(APPLE) AND NOT(DISABLE_DBUS)) set(Kirigami_EXTRA_LIBS Qt5::DBus) else() set(Kirigami_EXTRA_LIBS "") endif() else(STATIC_LIBRARY) set(Kirigami_EXTRA_LIBS KF5::Kirigami2) endif(STATIC_LIBRARY) target_link_libraries(kirigamiplugin PUBLIC Qt5::Core PRIVATE ${Kirigami_EXTRA_LIBS} Qt5::Qml Qt5::Quick Qt5::QuickControls2 ) if (NOT STATIC_LIBRARY) add_custom_target(copy) file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/bin/org/kde/kirigami.2) add_custom_command(TARGET copy PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/controls ${CMAKE_BINARY_DIR}/bin/org/kde/kirigami.2/) add_custom_command(TARGET copy PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/styles ${CMAKE_BINARY_DIR}/bin/org/kde/kirigami.2/styles) add_dependencies(kirigamiplugin copy) install(DIRECTORY controls/ DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/kirigami.2) if(Qt5Qml_VERSION VERSION_LESS 5.10) install(FILES controls/private/ActionMenuItemQt59.qml DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/kirigami.2/private RENAME ActionMenuItem.qml) else() install(FILES controls/private/ActionMenuItemQt510.qml DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/kirigami.2/private RENAME ActionMenuItem.qml) endif() if (PLASMA_ENABLED) install(DIRECTORY styles/Plasma DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/kirigami.2/styles) endif() if (DESKTOP_ENABLED) install(DIRECTORY styles/org.kde.desktop DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/kirigami.2/styles) endif() if (PLASMA_ENABLED AND DESKTOP_ENABLED) install(DIRECTORY styles/org.kde.desktop.plasma DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/kirigami.2/styles) endif() install(DIRECTORY styles/Material DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/kirigami.2/styles) install(FILES ${platformspecific} DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/kirigami.2) include(ECMGeneratePriFile) ecm_generate_pri_file(BASE_NAME Kirigami2 LIB_NAME KF5Kirigami2 DEPS "core qml quick svg" FILENAME_VAR PRI_FILENAME ) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) endif(NOT STATIC_LIBRARY) install(TARGETS kirigamiplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/kirigami.2) diff --git a/src/controls/private/AbstractPageHeader.qml b/src/controls/private/AbstractPageHeader.qml index 04510da2..94f34606 100644 --- a/src/controls/private/AbstractPageHeader.qml +++ b/src/controls/private/AbstractPageHeader.qml @@ -1,42 +1,44 @@ /* * Copyright 2018 Marco Martin * * 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 Library 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.5 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.2 -import org.kde.kirigami 2.4 +import org.kde.kirigami 2.5 AbstractApplicationHeader { + id: root anchors.fill: parent property Item container property bool current minimumHeight: pageRow.globalToolBar.minimumHeight maximumHeight: pageRow.globalToolBar.maximumHeight preferredHeight: pageRow.globalToolBar.preferredHeight implicitHeight: page.y - leftPadding: Qt.application.layoutDirection == Qt.LeftToRight - ? Math.max(0, pageRow.contentItem.contentX - mapToItem(pageRow.contentItem.contentItem, 0, 0).x + pageRow.globalToolBar.leftReservedSpace) - : Math.max(0, mapToItem(pageRow.contentItem.contentItem, width, 0).x - (pageRow.contentItem.contentX + pageRow.width) + pageRow.globalToolBar.leftReservedSpace) + leftPadding: Math.min(Qt.application.layoutDirection == Qt.LeftToRight + ? Math.max(0, pageRow.ScenePosition.x - page.ScenePosition.x + pageRow.globalToolBar.leftReservedSpace) + : Math.max(0, -pageRow.width + pageRow.ScenePosition.x + page.ScenePosition.x + page.width + pageRow.globalToolBar.leftReservedSpace), + root.width/2) rightPadding: Qt.application.layoutDirection == Qt.LeftToRight - ? Math.min(pageRow.globalToolBar.rightReservedSpace, Math.max(0, mapToItem(pageRow.contentItem.contentItem, width, 0).x - (pageRow.contentItem.contentX + pageRow.width) + pageRow.globalToolBar.rightReservedSpace)) - : Math.max(0, pageRow.contentItem.contentX - mapToItem(pageRow.contentItem.contentItem, 0, 0).x + pageRow.globalToolBar.rightReservedSpace) + ? Math.min(-pageRow.width + pageRow.ScenePosition.x + page.ScenePosition.x + page.width + pageRow.globalToolBar.rightReservedSpace) + : Math.max(0, pageRow.ScenePosition.x - page.ScenePosition.x + pageRow.globalToolBar.rightReservedSpace) } diff --git a/src/controls/private/PageRowGlobalToolBarUI.qml b/src/controls/private/PageRowGlobalToolBarUI.qml index a482d357..aa21760e 100644 --- a/src/controls/private/PageRowGlobalToolBarUI.qml +++ b/src/controls/private/PageRowGlobalToolBarUI.qml @@ -1,96 +1,97 @@ /* * Copyright 2018 Marco Martin * * 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 Library 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.1 import QtQuick.Layouts 1.2 import org.kde.kirigami 2.4 as Kirigami import "../templates/private" as TemplatesPrivate Kirigami.AbstractApplicationHeader { id: header readonly property int leftReservedSpace: buttonsLayout.visible && buttonsLayout.visibleChildren.length > 1 ? buttonsLayout.width : 0 readonly property int rightReservedSpace: rightHandleAnchor.visible ? backButton.background.implicitHeight : 0 readonly property alias leftHandleAnchor: leftHandleAnchor readonly property alias rightHandleAnchor: rightHandleAnchor height: visible ? implicitHeight : 0 minimumHeight: globalToolBar.minimumHeight preferredHeight: globalToolBar.preferredHeight maximumHeight: globalToolBar.maximumHeight RowLayout { anchors.fill: parent spacing: 0 RowLayout { id: buttonsLayout visible: globalToolBar.showNavigationButtons && globalToolBar.actualStyle != Kirigami.ApplicationHeaderStyle.None Item { id: leftHandleAnchor - visible: typeof applicationWindow() !== "undefined" && applicationWindow().globalDrawer.handleVisible && applicationWindow().globalDrawer.handle.handleAnchor == leftHandleAnchor + visible: typeof applicationWindow() !== "undefined" && applicationWindow().globalDrawer.handleVisible && + (applicationWindow().globalDrawer.handle.handleAnchor == Qt.application.layoutDirection == Qt.LeftToRight ? leftHandleAnchor : rightHandleAnchor) Layout.preferredWidth: backButton.background.implicitHeight Layout.preferredHeight: backButton.background.implicitHeight } TemplatesPrivate.BackButton { id: backButton Layout.leftMargin: leftHandleAnchor.visible ? 0 : Kirigami.Units.smallSpacing Layout.preferredWidth: background.implicitHeight Layout.preferredHeight: background.implicitHeight } TemplatesPrivate.ForwardButton { Layout.preferredWidth: background.implicitHeight Layout.preferredHeight: background.implicitHeight } Kirigami.Separator { Layout.preferredHeight: parent.parent.height * 0.6 //FIXME: hacky opacity: buttonsLayout.visibleChildren.length > 1 } } Loader { id: breadcrumbLoader Layout.fillWidth: true Layout.fillHeight: true active: globalToolBar.actualStyle == Kirigami.ApplicationHeaderStyle.TabBar || globalToolBar.actualStyle == Kirigami.ApplicationHeaderStyle.Breadcrumb //TODO: different implementation? sourceComponent: Kirigami.ApplicationHeader { minimumHeight: height preferredHeight: height maximumHeight: height backButtonEnabled: false anchors.fill:parent background.visible: false headerStyle: globalToolBar.style } } Item { id: rightHandleAnchor - visible: typeof applicationWindow() !== "undefined" && applicationWindow().contextDrawer && applicationWindow().contextDrawer.handleVisible && applicationWindow().contextDrawer.handle.handleAnchor == rightHandleAnchor + visible: typeof applicationWindow() !== "undefined" && applicationWindow().contextDrawer && applicationWindow().contextDrawer.handleVisible && applicationWindow().contextDrawer.handle.handleAnchor == Qt.application.layoutDirection == Qt.LeftToRight ? rightHandleAnchor : leftHandleAnchor Layout.preferredWidth: backButton.background.implicitHeight Layout.preferredHeight: backButton.background.implicitHeight } } background.visible: breadcrumbLoader.active } diff --git a/src/controls/templates/OverlayDrawer.qml b/src/controls/templates/OverlayDrawer.qml index f325d44b..66c27527 100644 --- a/src/controls/templates/OverlayDrawer.qml +++ b/src/controls/templates/OverlayDrawer.qml @@ -1,443 +1,440 @@ /* * Copyright 2012 Marco Martin * * 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 Library 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.1 import QtQuick.Templates 2.0 as T2 -import org.kde.kirigami 2.4 +import org.kde.kirigami 2.5 import "private" /** * Overlay Drawers are used to expose additional UI elements needed for * small secondary tasks for which the main UI elements are not needed. * For example in Okular Active, an Overlay Drawer is used to display * thumbnails of all pages within a document along with a search field. * This is used for the distinct task of navigating to another page. * @inherits: QtQuick.Templates.Drawer */ T2.Drawer { id: root z: modal ? (Math.round((position * 100) / 10) ): 0 //BEGIN Properties /** * drawerOpen: bool * true when the drawer is open and visible */ property bool drawerOpen: false /** * enabled: bool * This property holds whether the item receives mouse and keyboard events. By default this is true. */ property bool enabled: true /** * peeking: true * When true the drawer is in a state between open and closed. the drawer is visible but not completely open. * This is usually the case when the user is dragging the drawer from a screen * edge, so the user is "peeking" what's in the drawer */ property bool peeking: false /** * animating: Bool * True during an animation of a drawer either opening or closing */ readonly property bool animating : enterAnimation.animating || exitAnimation.animating || positionResetAnim.running /** * collapsible: Bool * When true, the drawer can be collapsed to a very thin, usually icon only sidebar. * Only modal drawers are collapsible. * Collapsible is not supported in Mobile mode * @since 2.5 */ property bool collapsible: false /** * collapsed: bool * When true, the drawer will be collapsed to a very thin sidebar, * usually icon only. * Only collapsible drawers can be collapsed */ property bool collapsed: false /** * collapsedSize: int * When collapsed, the drawer will be resized to this size * (which may be width for vertical drawers or height for * horizontal drawers). * By default it's just enough to accomodate medium sized icons */ property int collapsedSize: Units.iconSizes.medium + Units.smallSpacing * 2 /** * A grouped property describing an optional icon. * * source: The source of the icon, a freedesktop-compatible icon name is recommended. * * color: An optional tint color for the icon. * * If no custom icon is set, a menu icon is shown for the application globalDrawer * and an overflow menu icon is shown for the contextDrawer. * That's the default for the GlobalDrawer and ContextDrawer components respectively. * * For OverlayDrawer the default is view-right-close or view-left-close depending on the drawer location * @since 2.5 */ readonly property QtObject handleOpenIcon: IconPropertiesGroup {source: root.edge == Qt.RightEdge ? "view-right-close" : "view-left-close"} /** * A grouped property describing an optional icon. * * source: The source of the icon, a freedesktop-compatible icon name is recommended. * * color: An optional tint color for the icon. * * If no custom icon is set, an X icon is shown, * which will morph into the Menu or overflow icons * * For OverlayDrawer the default is view-right-new or view-left-new depending on the drawer location * @since 2.5 */ readonly property QtObject handleClosedIcon: IconPropertiesGroup {source: root.edge == Qt.RightEdge ? "view-right-new" : "view-left-new"} /** * handleVisible: bool * If true, a little handle will be visible to make opening the drawer easier * Currently supported only on left and right drawers */ property bool handleVisible: typeof(applicationWindow)===typeof(Function) && applicationWindow() ? applicationWindow().controlsVisible : true /** * handle: Item * Readonly property that points to the item that will act as a physical * handle for the Drawer **/ readonly property Item handle: MouseArea { id: drawerHandle z: root.modal ? applicationWindow().overlay.z + (root.position > 0 ? +1 : -1) : root.background.parent.z + 1 preventStealing: true hoverEnabled: handleAnchor parent: applicationWindow().overlay.parent property Item handleAnchor: (!Settings.isMobile && applicationWindow().pageStack && applicationWindow().pageStack.globalToolBar && applicationWindow().pageStack.globalToolBar.actualStyle != ApplicationHeaderStyle.None) ? (root.edge == Qt.LeftEdge ? applicationWindow().pageStack.globalToolBar.leftHandleAnchor : applicationWindow().pageStack.globalToolBar.rightHandleAnchor) : (applicationWindow().header && applicationWindow().header.toString().indexOf("ToolBarApplicationHeader") !== -1 ? applicationWindow().header : null) - //FIXME: temporary solution, we need a scenepostracker item - onHandleAnchorChanged: { - handleAnchor.parent.yChanged.connect(handleAnchor.yChanged); - } + property int startX property int mappedStartX enabled: root.handleVisible onPressed: { root.peeking = true; startX = mouse.x; mappedStartX = mapToItem(parent, startX, 0).x } onPositionChanged: { if (!pressed) { return; } var pos = mapToItem(parent, mouse.x - startX, mouse.y); switch(root.edge) { case Qt.LeftEdge: root.position = pos.x/root.contentItem.width; break; case Qt.RightEdge: root.position = (root.parent.width - pos.x - width)/root.contentItem.width; break; default: } } onReleased: { root.peeking = false; if (Math.abs(mapToItem(parent, mouse.x, 0).x - mappedStartX) < Qt.styleHints.startDragDistance) { if (!root.drawerOpen) { root.close(); } root.drawerOpen = !root.drawerOpen; } } onCanceled: { root.peeking = false } x: { switch(root.edge) { case Qt.LeftEdge: return root.background.width * root.position + Units.smallSpacing; case Qt.RightEdge: return drawerHandle.parent.width - (root.background.width * root.position) - width - Units.smallSpacing; default: return 0; } } - y: handleAnchor && anchors.bottom ? handleAnchor.parent.mapToItem(root.contentItem, 0, handleAnchor.y).y : 0 + y: handleAnchor && anchors.bottom ? handleAnchor.ScenePosition.y : 0 anchors { bottom: drawerHandle.handleAnchor ? undefined : parent.bottom bottomMargin: { if (!applicationWindow()) { return; } var margin = 0; if (applicationWindow().footer) { margin = applicationWindow().footer.height; } if (!applicationWindow() || !applicationWindow().pageStack || !applicationWindow().pageStack.contentItem || !applicationWindow().pageStack.contentItem.itemAt) { return margin; } var item; if (applicationWindow().pageStack.layers.depth > 1) { item = applicationWindow().pageStack.layers.currentItem; } else { item = applicationWindow().pageStack.contentItem.itemAt(applicationWindow().pageStack.contentItem.contentX + drawerHandle.x, 0); } //try to take the last item if (!item) { item = applicationWindow().pageStack.lastItem; } var pageFooter = item && item.page ? item.page.footer : (item ? item.footer : undefined); if (pageFooter) { margin += pageFooter.height; } return margin; } Behavior on bottomMargin { NumberAnimation { duration: Units.shortDuration easing.type: Easing.InOutQuad } } } visible: root.enabled && (root.edge == Qt.LeftEdge || root.edge == Qt.RightEdge) width: handleAnchor ? handleAnchor.width : Units.iconSizes.medium + Units.smallSpacing*2 height: handleAnchor ? handleAnchor.height : width opacity: root.handleVisible ? 1 : 0 Behavior on opacity { NumberAnimation { duration: Units.longDuration easing.type: Easing.InOutQuad } } transform: Translate { id: translateTransform x: root.handleVisible ? 0 : (root.edge == Qt.LeftEdge ? -drawerHandle.width : drawerHandle.width) Behavior on x { NumberAnimation { duration: Units.longDuration easing.type: !root.handleVisible ? Easing.OutQuad : Easing.InQuad } } } } Theme.colorSet: modal ? Theme.View : Theme.Window Theme.onColorSetChanged: { contentItem.Theme.colorSet = Theme.colorSet background.Theme.colorSet = Theme.colorSet } //END Properties //BEGIN reassign properties //default paddings leftPadding: Units.smallSpacing topPadding: Units.smallSpacing rightPadding: Units.smallSpacing bottomPadding: Units.smallSpacing parent: modal ? T2.ApplicationWindow.overlay : T2.ApplicationWindow.contentItem edge: Qt.LeftEdge modal: true dragMargin: enabled && (edge == Qt.LeftEdge || edge == Qt.RightEdge) ? Qt.styleHints.startDragDistance : 0 contentWidth: contentItem.implicitWidth || (contentChildren.length === 1 ? contentChildren[0].implicitWidth : 0) contentHeight: contentItem.implicitHeight || (contentChildren.length === 1 ? contentChildren[0].implicitHeight : 0) enter: Transition { SequentialAnimation { id: enterAnimation /*NOTE: why this? the running status of the enter transition is not relaible and * the SmoothedAnimation is always marked as non running, * so the only way to get to a reliable animating status is with this */ property bool animating ScriptAction { script: { enterAnimation.animating = true //on non modal dialog we don't want drawers in the overlay if (!root.modal) { root.background.parent.parent = applicationWindow().overlay.parent } } } SmoothedAnimation { velocity: 5 } ScriptAction { script: enterAnimation.animating = false } } } exit: Transition { SequentialAnimation { id: exitAnimation property bool animating ScriptAction { script: exitAnimation.animating = true } SmoothedAnimation { velocity: 5 } ScriptAction { script: exitAnimation.animating = false } } } //END reassign properties //BEGIN signal handlers onCollapsedChanged: { if ((!collapsible || modal) && collapsed) { collapsed = true; } } onCollapsibleChanged: { if (!collapsible) { collapsed = false; } else if (modal) { collapsible = false; } } onModalChanged: { if (modal) { collapsible = false; } } onPositionChanged: { if (peeking) { visible = true } } onVisibleChanged: { if (peeking) { visible = true } else { drawerOpen = visible; } } onPeekingChanged: { if (peeking) { root.enter.enabled = false; root.exit.enabled = false; } else { positionResetAnim.to = position > 0.5 ? 1 : 0; positionResetAnim.running = true root.enter.enabled = true; root.exit.enabled = true; } } onDrawerOpenChanged: { //sync this property only when the component is properly loaded if (!__internal.completed) { return; } positionResetAnim.running = false; if (drawerOpen) { open(); } else { close(); } } Component.onCompleted: { //if defined as drawerOpen by default in QML, don't animate if (root.drawerOpen) { root.enter.enabled = false; root.visible = true; root.position = 1; root.enter.enabled = true; } __internal.completed = true; contentItem.Theme.colorSet = Theme.colorSet; background.Theme.colorSet = Theme.colorSet; } //END signal handlers //this is as hidden as it can get here property QtObject __internal: QtObject { //here in order to not be accessible from outside property bool completed: false property NumberAnimation positionResetAnim: NumberAnimation { id: positionResetAnim target: root to: 0 property: "position" duration: (root.position)*Units.longDuration } readonly property Item statesItem: Item { states: [ State { when: root.collapsed PropertyChanges { target: root implicitWidth: edge == Qt.TopEdge || edge == Qt.BottomEdge ? applicationWindow().width : Math.min(collapsedSize, Math.round(applicationWindow().width*0.8)) implicitHeight: edge == Qt.LeftEdge || edge == Qt.RightEdge ? applicationWindow().height : Math.min(collapsedSize, Math.round(applicationWindow().height*0.8)) } }, State { when: !root.collapsed PropertyChanges { target: root implicitWidth: edge == Qt.TopEdge || edge == Qt.BottomEdge ? applicationWindow().width : Math.min(contentItem.implicitWidth, Math.round(applicationWindow().width*0.8)) implicitHeight: edge == Qt.LeftEdge || edge == Qt.RightEdge ? applicationWindow().height : Math.min(contentItem.implicitHeight, Math.round(applicationWindow().height*0.8)) } } ] transitions: Transition { reversible: true NumberAnimation { properties: "implicitWidth,implicitHeight" duration: Units.longDuration easing.type: Easing.InOutQuad } } } } } diff --git a/src/kirigamiplugin.cpp b/src/kirigamiplugin.cpp index 311ae21e..31ddaad1 100644 --- a/src/kirigamiplugin.cpp +++ b/src/kirigamiplugin.cpp @@ -1,179 +1,181 @@ /* * Copyright 2009 by Alan Alpert * Copyright 2010 by Ménard Alexis * Copyright 2010 by Marco Martin * 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 Library 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. */ #include "kirigamiplugin.h" #include "enums.h" #include "desktopicon.h" #include "settings.h" #include "formlayoutattached.h" #include "mnemonicattached.h" #include "delegaterecycler.h" +#include "scenepositionattached.h" #include #include #include #include #include "libkirigami/platformtheme.h" static QString s_selectedStyle; //Q_INIT_RESOURCE(kirigami); #ifdef KIRIGAMI_BUILD_TYPE_STATIC #include #endif QUrl KirigamiPlugin::componentUrl(const QString &fileName) const { foreach (const QString &style, m_stylesFallbackChain) { const QString candidate = QStringLiteral("styles/") + style + QLatin1Char('/') + fileName; if (QFile::exists(resolveFilePath(candidate))) { #ifdef KIRIGAMI_BUILD_TYPE_STATIC return QUrl(QStringLiteral("qrc:/org/kde/kirigami/styles/") + style + QLatin1Char('/') + fileName); #else return QUrl(resolveFileUrl(candidate)); #endif } } #ifdef KIRIGAMI_BUILD_TYPE_STATIC return QUrl(QStringLiteral("qrc:/org/kde/kirigami/") + fileName); #else return QUrl(resolveFileUrl(fileName)); #endif } void KirigamiPlugin::registerTypes(const char *uri) { Q_ASSERT(uri == QLatin1String("org.kde.kirigami")); const QString style = QQuickStyle::name(); #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) //org.kde.desktop.plasma is a couple of files that fall back to desktop by purpose if ((style.isEmpty() || style == QStringLiteral("org.kde.desktop.plasma")) && QFile::exists(resolveFilePath(QStringLiteral("/styles/org.kde.desktop")))) { m_stylesFallbackChain.prepend(QStringLiteral("org.kde.desktop")); } #elif defined(Q_OS_ANDROID) if (!m_stylesFallbackChain.contains(QStringLiteral("Material"))) { m_stylesFallbackChain.prepend(QStringLiteral("Material")); } #else // do we have an iOS specific style? if (!m_stylesFallbackChain.contains(QStringLiteral("Material"))) { m_stylesFallbackChain.prepend(QStringLiteral("Material")); } #endif if (!style.isEmpty() && QFile::exists(resolveFilePath(QStringLiteral("/styles/") + style)) && !m_stylesFallbackChain.contains(style)) { m_stylesFallbackChain.prepend(style); //if we have plasma deps installed, use them for extra integration if (style == QStringLiteral("org.kde.desktop") && QFile::exists(resolveFilePath(QStringLiteral("/styles/org.kde.desktop.plasma")))) { m_stylesFallbackChain.prepend("org.kde.desktop.plasma"); } } else { #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) m_stylesFallbackChain.prepend(QStringLiteral("org.kde.desktop")); #endif } //At this point the fallback chain will be selected->org.kde.desktop->Fallback s_selectedStyle = m_stylesFallbackChain.first(); qmlRegisterSingletonType(uri, 2, 0, "Settings", [](QQmlEngine*, QJSEngine*) -> QObject* { Settings *settings = new Settings; settings->setStyle(s_selectedStyle); return settings; } ); qmlRegisterUncreatableType(uri, 2, 0, "ApplicationHeaderStyle", "Cannot create objects of type ApplicationHeaderStyle"); //old legacy retrocompatible Theme qmlRegisterSingletonType(componentUrl(QStringLiteral("Theme.qml")), uri, 2, 0, "Theme"); qmlRegisterSingletonType(componentUrl(QStringLiteral("Units.qml")), uri, 2, 0, "Units"); qmlRegisterType(componentUrl(QStringLiteral("Action.qml")), uri, 2, 0, "Action"); qmlRegisterType(componentUrl(QStringLiteral("AbstractApplicationHeader.qml")), uri, 2, 0, "AbstractApplicationHeader"); qmlRegisterType(componentUrl(QStringLiteral("AbstractApplicationWindow.qml")), uri, 2, 0, "AbstractApplicationWindow"); qmlRegisterType(componentUrl(QStringLiteral("AbstractListItem.qml")), uri, 2, 0, "AbstractListItem"); qmlRegisterType(componentUrl(QStringLiteral("ApplicationHeader.qml")), uri, 2, 0, "ApplicationHeader"); qmlRegisterType(componentUrl(QStringLiteral("ToolBarApplicationHeader.qml")), uri, 2, 0, "ToolBarApplicationHeader"); qmlRegisterType(componentUrl(QStringLiteral("ApplicationWindow.qml")), uri, 2, 0, "ApplicationWindow"); qmlRegisterType(componentUrl(QStringLiteral("BasicListItem.qml")), uri, 2, 0, "BasicListItem"); qmlRegisterType(componentUrl(QStringLiteral("OverlayDrawer.qml")), uri, 2, 0, "OverlayDrawer"); qmlRegisterType(componentUrl(QStringLiteral("ContextDrawer.qml")), uri, 2, 0, "ContextDrawer"); qmlRegisterType(componentUrl(QStringLiteral("GlobalDrawer.qml")), uri, 2, 0, "GlobalDrawer"); qmlRegisterType(componentUrl(QStringLiteral("Heading.qml")), uri, 2, 0, "Heading"); qmlRegisterType(componentUrl(QStringLiteral("Separator.qml")), uri, 2, 0, "Separator"); qmlRegisterType(componentUrl(QStringLiteral("PageRow.qml")), uri, 2, 0, "PageRow"); //we want a different implementation with Plasma style if (s_selectedStyle == QStringLiteral("Plasma")) { qmlRegisterType(componentUrl(QStringLiteral("Icon.qml")), uri, 2, 0, "Icon"); } else { DesktopIcon::s_internalIconPath = resolveFilePath(QStringLiteral("icons")); qmlRegisterType(uri, 2, 0, "Icon"); } qmlRegisterType(componentUrl(QStringLiteral("Label.qml")), uri, 2, 0, "Label"); //TODO: uncomment for 2.3 release //qmlRegisterTypeNotAvailable(uri, 2, 3, "Label", "Label type not supported anymore, use QtQuick.Controls.Label 2.0 instead"); qmlRegisterType(componentUrl(QStringLiteral("OverlaySheet.qml")), uri, 2, 0, "OverlaySheet"); qmlRegisterType(componentUrl(QStringLiteral("Page.qml")), uri, 2, 0, "Page"); qmlRegisterType(componentUrl(QStringLiteral("ScrollablePage.qml")), uri, 2, 0, "ScrollablePage"); qmlRegisterType(componentUrl(QStringLiteral("SplitDrawer.qml")), uri, 2, 0, "SplitDrawer"); qmlRegisterType(componentUrl(QStringLiteral("SwipeListItem.qml")), uri, 2, 0, "SwipeListItem"); //2.1 qmlRegisterType(componentUrl(QStringLiteral("AbstractItemViewHeader.qml")), uri, 2, 1, "AbstractItemViewHeader"); qmlRegisterType(componentUrl(QStringLiteral("ItemViewHeader.qml")), uri, 2, 1, "ItemViewHeader"); qmlRegisterType(componentUrl(QStringLiteral("AbstractApplicationItem.qml")), uri, 2, 1, "AbstractApplicationItem"); qmlRegisterType(componentUrl(QStringLiteral("ApplicationItem.qml")), uri, 2, 1, "ApplicationItem"); //2.2 //Theme changed from a singleton to an attached property qmlRegisterUncreatableType(uri, 2, 2, "Theme", "Cannot create objects of type Theme, use it as an attached poperty"); //2.3 qmlRegisterType(componentUrl(QStringLiteral("FormLayout.qml")), uri, 2, 3, "FormLayout"); qmlRegisterUncreatableType(uri, 2, 3, "FormData", "Cannot create objects of type FormData, use it as an attached poperty"); qmlRegisterUncreatableType(uri, 2, 3, "MnemonicData", "Cannot create objects of type MnemonicData, use it as an attached poperty"); //2.4 qmlRegisterType(componentUrl(QStringLiteral("AbstractCard.qml")), uri, 2, 4, "AbstractCard"); qmlRegisterType(componentUrl(QStringLiteral("Card.qml")), uri, 2, 4, "Card"); qmlRegisterType(componentUrl(QStringLiteral("CardsListView.qml")), uri, 2, 4, "CardsListView"); qmlRegisterType(componentUrl(QStringLiteral("CardsGridView.qml")), uri, 2, 4, "CardsGridView"); qmlRegisterType(componentUrl(QStringLiteral("CardsLayout.qml")), uri, 2, 4, "CardsLayout"); qmlRegisterType(componentUrl(QStringLiteral("InlineMessage.qml")), uri, 2, 4, "InlineMessage"); qmlRegisterUncreatableType(uri, 2, 4, "MessageType", "Cannot create objects of type MessageType"); qmlRegisterType(uri, 2, 4, "DelegateRecycler"); //2.5 qmlRegisterType(componentUrl(QStringLiteral("ListItemDragHandle.qml")), uri, 2, 5, "ListItemDragHandle"); qmlRegisterType(componentUrl(QStringLiteral("ActionToolBar.qml")), uri, 2, 5, "ActionToolBar"); + qmlRegisterUncreatableType(uri, 2, 5, "ScenePosition", "Cannot create objects of type ScenePosition, use it as an attached poperty"); qmlProtectModule(uri, 2); } #include "moc_kirigamiplugin.cpp" diff --git a/src/scenepositionattached.cpp b/src/scenepositionattached.cpp new file mode 100644 index 00000000..f67ec9e8 --- /dev/null +++ b/src/scenepositionattached.cpp @@ -0,0 +1,79 @@ +/* +* Copyright (C) 2017 by Marco Martin +* +* 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 Library 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. +*/ + +#include "scenepositionattached.h" +#include +#include + +ScenePositionAttached::ScenePositionAttached(QObject *parent) + : QObject(parent) +{ + m_item = qobject_cast(parent); + connectAncestors(m_item); +} + +ScenePositionAttached::~ScenePositionAttached() +{ +} + +int ScenePositionAttached::x() const +{ + return m_item->mapToScene(QPointF()).x(); +} + +int ScenePositionAttached::y() const +{ + return m_item->mapToScene(QPointF()).y(); +} + +void ScenePositionAttached::connectAncestors(QQuickItem *item) +{ + if (!item) { + return; + } + + QQuickItem *ancestor = item; + while (ancestor) { + m_ancestors << ancestor; + + connect(ancestor, &QQuickItem::xChanged, this, &ScenePositionAttached::xChanged); + connect(ancestor, &QQuickItem::yChanged, this, &ScenePositionAttached::yChanged); + connect(ancestor, &QQuickItem::parentChanged, this, + [this, ancestor]() { + do { + disconnect(ancestor, 0, this, 0); + m_ancestors.pop_back(); + } while (m_ancestors.last() != ancestor); + + connectAncestors(ancestor); + emit xChanged(); + emit yChanged(); + } + ); + + ancestor = ancestor->parentItem(); + } +} + +ScenePositionAttached *ScenePositionAttached::qmlAttachedProperties(QObject *object) +{ + return new ScenePositionAttached(object); +} + +#include "moc_scenepositionattached.cpp" diff --git a/src/scenepositionattached.h b/src/scenepositionattached.h new file mode 100644 index 00000000..d96ed003 --- /dev/null +++ b/src/scenepositionattached.h @@ -0,0 +1,76 @@ +/* +* Copyright (C) 2018 by Marco Martin +* +* 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 Library 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. +*/ + +#ifndef SCENEPOSITIONATTACHED_H +#define SCENEPOSITIONATTACHED_H + +#include +#include + +class QQuickItem; + +/** + * This attached property contains the information about the scene position of the item: + * Its global x and y coordinates will update automatically and can be binded + * @code + * import org.kde.kirigami 2.5 as Kirigami + * Text { + * text: ScenePosition.x + * } + * @endcode + * @since 2.3 + */ +class ScenePositionAttached : public QObject +{ + Q_OBJECT + /** + * The global scene X position + */ + Q_PROPERTY(int x READ x NOTIFY xChanged) + + /** + * The global scene Y position + */ + Q_PROPERTY(int y READ y NOTIFY yChanged) + +public: + + explicit ScenePositionAttached(QObject *parent = nullptr); + ~ScenePositionAttached() override; + + int x() const; + int y() const; + + //QML attached property + static ScenePositionAttached *qmlAttachedProperties(QObject *object); + +Q_SIGNALS: + void xChanged(); + void yChanged(); + +private: + void connectAncestors(QQuickItem *item); + + QQuickItem *m_item = nullptr; + QList m_ancestors; +}; + +QML_DECLARE_TYPEINFO(ScenePositionAttached, QML_HAS_ATTACHED_PROPERTIES) + +#endif // SCENEPOSITIONATTACHED_H