diff --git a/src/genericutils/qml/outlinebutton.qml b/src/genericutils/qml/outlinebutton.qml index b9e37306..108e8109 100644 --- a/src/genericutils/qml/outlinebutton.qml +++ b/src/genericutils/qml/outlinebutton.qml @@ -1,201 +1,202 @@ /*************************************************************************** * Copyright (C) 2017-2018 by Bluesystems * * Author : Emmanuel Lepage Vallee * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * **************************************************************************/ import QtQuick 2.0 import org.kde.kirigami 2.2 as Kirigami Item { id: button property color color: Kirigami.Theme.textColor property string label: "" property real expandedHeight: height property real radius: Math.min(48, button.height) / 2 property real topPadding: (button.height - (radius*2)) / 2 property real sideMargin: 10 property var alignment: Qt.AlignHCenter property alias icon: icn.source + property real preferredExpandedWidth: label.implicitWidth + button.radius*2 implicitWidth: height width: implicitWidth signal clicked() Rectangle { id: addCallButton clip: true /** * Handle both when there is a single button + alignment and when it is * part of a group. */ anchors.horizontalCenter: button.alignment == Qt.AlignHCenter ? parent.horizontalCenter : undefined anchors.right: button.alignment == Qt.AlignRight ? parent.right : undefined anchors.left: button.alignment == Qt.AlignLeft ? parent.left : undefined visible: false y: button.topPadding height: button.radius*2 width: button.radius*2 radius: button.radius color: "transparent" border.width: 1 border.color: button.color opacity: 0 Rectangle { id: background anchors.fill: parent color: button.color opacity: 0 Behavior on opacity { NumberAnimation {duration: 300; easing.type: Easing.OutQuad } } } Behavior on width { NumberAnimation {duration: 200; easing.type: Easing.OutQuad } } Behavior on height { NumberAnimation {duration: 200; easing.type: Easing.OutQuad } } Behavior on radius { NumberAnimation {duration: 100; easing.type: Easing.OutQuad } } Behavior on opacity { NumberAnimation {duration: 200; easing.type: Easing.OutQuad } } Item { id: icon width: button.radius*2 height: button.radius*2 anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left Rectangle { visible: icn.source == "" width: 1 height: button.radius anchors.centerIn: parent color: button.color } Rectangle { visible: icn.source == "" height: 1 width: button.radius anchors.centerIn: parent color: button.color } Image { id: icn width: 16//Math.sqrt(2*parent.width) // the largest square fitting in a circle height: width sourceSize.width: width sourceSize.height: width anchors.centerIn: parent } } Text { id: label opacity: 0 color: button.color text: button.label font.pointSize: Kirigami.Theme.defaultFont.pointSize*1.6 Behavior on opacity { NumberAnimation {duration: 200; easing.type: Easing.OutQuad } } // Avoid overlapping the icon when the width isn't large enough anchors.centerIn: implicitWidth > (button.width) ? undefined : parent anchors.right: implicitWidth > (button.width) ? addCallButton.right : undefined } MouseArea { id: mouseGrabber anchors.fill: parent hoverEnabled: true onClicked: { button.clicked() } } StateGroup { id: stateGroup states: [ State { name: "hover" when: mouseGrabber.containsMouse extend: "active" PropertyChanges { target: addCallButton opacity: 0.8 radius: 5 height: button.expandedHeight width: addCallButton.parent.width - 2*button.sideMargin } PropertyChanges { target: background opacity: 0.1 } PropertyChanges { target: label opacity: 0.8 } PropertyChanges { target: button height: button.expandedHeight implicitWidth: label.implicitWidth + 2*height + 2 } }, State { name: "active" when: button.visible PropertyChanges { target: addCallButton visible: true opacity: 0.5 radius: 99 width: button.radius*2 } PropertyChanges { target: background opacity: 0 } PropertyChanges { target: label opacity: 0 } PropertyChanges { target: button implicitWidth: height } } ] } } } diff --git a/src/jamisearch/qml/searchbox.qml b/src/jamisearch/qml/searchbox.qml index 75e08736..5feb9994 100644 --- a/src/jamisearch/qml/searchbox.qml +++ b/src/jamisearch/qml/searchbox.qml @@ -1,182 +1,192 @@ /*************************************************************************** * Copyright (C) 2018 by Bluesystems * * Author : Emmanuel Lepage Vallee * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * **************************************************************************/ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.0 import org.kde.kirigami 2.2 as Kirigami import net.lvindustries.ringqtquick 1.0 as RingQtQuick Item { id: searchBox property bool empty: search.text == "" property var searchView: null - property real xPadding: 2*Kirigami.Units.largeSpacing +2 /*border*/ + property real xPadding: 2*Kirigami.Units.largeSpacing + 2 /*border*/ property alias searchFocus: search.focus property alias labelWidth: findLabel.implicitWidth + property alias animationFinished: search.animationFinished Kirigami.Theme.colorSet: Kirigami.Theme.View function forceFocus() { search.forceActiveFocus(Qt.OtherFocusReason) } function hide() { search.text = "" search.focus = false if (RingSession.callModel.hasDialingCall) RingSession.callModel.dialingCall().performAction(RingQtQuick.Call.REFUSE) } FontMetrics { id: textMetric font: search.font } TextField { id: search + property bool animationFinished: x == (-(searchView.width-searchBox.width ) + xPadding) + // Auto complete an auto completion == fail inputMethodHints: Qt.ImhNoPredictiveText width: focus ? searchView.width - xPadding : parent.width x: focus ? -(searchView.width-searchBox.width ) + xPadding: 0 - y: (focus ? searchBox.height : 0) + Kirigami.Units.largeSpacing + y: focus ? + (searchBox.height + Kirigami.Units.largeSpacing) : + ((searchBox.height-search.height) / 2) leftPadding: Kirigami.Units.largeSpacing + 1 /*border*/ topPadding: Kirigami.Units.largeSpacing font.pointSize: Kirigami.Theme.defaultFont.pointSize*1.4 text: RingSession.callModel.hasDialingCall ? RingSession.callModel.dialingCall().dialNumber : "" Behavior on x { NumberAnimation {duration: 300} } Behavior on y { NumberAnimation {duration: 150} } Behavior on width { NumberAnimation {duration: 300} } background : Item { + // Unlike what the documentation claims, explicit size is required + // with the Material style otherwise the size is 0x0 on Qt 5.12 height: textMetric.height*1.5 + width: search.width + anchors.centerIn: search + Rectangle { height: parent.height - 2 // 1px border width: parent.width color: "transparent" border.width: 1 border.color: Kirigami.Theme.textColor opacity: search.focus ? 0.8 : 0.5 radius: search.text == "" && !search.focus ? 99 : 5 Behavior on opacity { NumberAnimation {duration: 100} } Behavior on radius { NumberAnimation {duration: 200; easing.type: Easing.OutQuad} } RowLayout { height: parent.height y: search.text == "" && !search.focus ? 0 : -search.height x: search.focus ? (searchView.width-searchBox.width)/2 : 0 spacing: 5 Behavior on opacity { NumberAnimation {duration: 100} } Behavior on y { NumberAnimation {duration: 200} } Item { width: 2 } Image { width: parent.height - 6 height: parent.height - 6 Layout.alignment: Qt.AlignVCenter sourceSize.height: parent.height - 6 sourceSize.width: parent.height - 6 source: "image://SymbolicColorizer/image://icon/file-search-symbolic" } Text { id: findLabel Layout.fillWidth: true color: Kirigami.Theme.textColor text: i18n("Find someone") Layout.alignment: Qt.AlignVCenter font.pixelSize: parent.height * 0.55 } } } } onTextChanged: { // Cache the text to avoid a binding loop when the dialing call // is created for the first time var text = search.text if (RingSession.callModel.hasDialingCall || text != "") { var call = RingSession.callModel.dialingCall() call.dialNumber = text } } Keys.onDownPressed: { searchView.currentIndex = (searchView.currentIndex == searchView.count - 1) ? 0 : searchView.currentIndex + 1 } Keys.onUpPressed: { searchView.currentIndex = (searchView.currentIndex == 0) ? searchView.count - 1 : searchView.currentIndex - 1 } Keys.onReturnPressed: { var cm = searchView.currentItem.contactMethod // Display an error message when the selected element doesn't exist if (!searchView.currentItem.isSelectable) { searchView.displayNotFoundMessage() return } if (!cm) return cm = RingSession.individualDirectory.fromTemporary(cm) //FIXME workflow.currentContactMethod = cm searchBox.hide() } Keys.onEscapePressed: { searchBox.hide() } Keys.onPressed: { if (event.key == Qt.Key_Backspace && search.text == "") searchBox.hide() } } } diff --git a/src/jamisearch/qml/searchoverlay.qml b/src/jamisearch/qml/searchoverlay.qml index a4952d7f..a1e41faa 100644 --- a/src/jamisearch/qml/searchoverlay.qml +++ b/src/jamisearch/qml/searchoverlay.qml @@ -1,286 +1,297 @@ /*************************************************************************** * Copyright (C) 2018 by Bluesystems * * Author : Emmanuel Lepage Vallee * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * **************************************************************************/ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.0 as Layouts import QtGraphicalEffects 1.0 import org.kde.kirigami 2.0 as Kirigami import net.lvindustries.ringqtquick 1.0 as RingQtQuick import org.kde.ringkde.jamisearch 1.0 as JamiSearch Item { id: seachOverlay visible: searchState.activeState || searchState.firstSearchState // Properties property var source: null property bool active: searchState.display property QtObject searchBox: null property bool displayWelcome: searchState.firstSearchState property alias currentItem: searchView.currentItem // used be the search box property alias currentIndex: searchView.currentIndex // used by keyboard nav // Signals signal contactMethodSelected(var cm) signal displayNotFoundMessage() // Functions function hide() { searchBox.hide() } SystemPalette { id: inactivePalette colorGroup: SystemPalette.Inactive } // The algorithm used to define what's visible. It is in an class because // the expression size was no longer maintainable. JamiSearch.State { id: searchState searchEmpty: (!searchBox) || searchBox.empty focussed: searchBox && searchBox.searchFocus onDisplayChanged: { if (display) { searchBox.searchFocus = true searchBox.forceFocus() } } } // Track if the user asked to never see some information ever again JamiSearch.TipModel { id: displayTips } // Add a blurry background ShaderEffectSource { id: effectSource visible: false sourceItem: seachOverlay.source anchors.fill: seachOverlay sourceRect: Qt.rect( 0, 0, seachOverlay.width, seachOverlay.height ) } // Background Item { id: burryOverlay visible: false opacity: 0 anchors.fill: parent clip: true Repeater { anchors.fill: parent model: 5 FastBlur { anchors.fill: parent source: effectSource radius: 30 } } Rectangle { anchors.fill: parent color: inactivePalette.highlight opacity: 0.75 } } // Header buttons Loader { height: active ? searchBox.height : 0 - width: parent.width - anchors.right: parent.parent.right - y: -searchBox.height + width: seachOverlay.width + anchors.right: seachOverlay.right + anchors.top: seachOverlay.top + anchors.rightMargin: Kirigami.Units.largeSpacing + anchors.topMargin: Kirigami.Units.largeSpacing clip: true active: searchState.activeState sourceComponent: JamiSearch.ToolBar {} } // Content Layouts.ColumnLayout { anchors.fill: parent spacing: 0 Item { Layouts.Layout.fillWidth: true Layouts.Layout.preferredHeight: 2*searchBox.height } // Display a welcome message when the user uses the app for the first time JamiSearch.FirstRun { id: firstRun visible: active active: searchState.displayWelcome Layouts.Layout.fillWidth: true Layouts.Layout.preferredHeight: active ? parent.height* 0.4 : 0 Layouts.Layout.maximumHeight: active ? parent.height* 0.4 : 0 } // Display an icon for each search categories and match the colors with // colored symbols in each search entry JamiSearch.MatchCategories { id: filterList active: searchState.displaySearchCategories opacity: searchView.count > 0 ? 1 : 0 Layouts.Layout.fillWidth: true Layouts.Layout.preferredHeight: active ? 30 + Kirigami.Units.fontMetrics.height*1.5 : 0 } // Display some tips and help to new users JamiSearch.SearchTip { - active: searchState.displaySearchHelp + active: searchState.displaySearchHelp && searchBox.animationFinished + + // This is there is it get hidden as soon as the search results appear + visible: active + opacity: visible ? 1 : 0 + Layouts.Layout.fillWidth: true Layouts.Layout.preferredHeight: active ? height : 0 Layouts.Layout.maximumHeight: active ? height : 0 + + Behavior on opacity { + NumberAnimation {duration: 100} + } } // Display the results for the current query JamiSearch.Results { id: searchView Layouts.Layout.fillWidth: true Layouts.Layout.fillHeight: true z: 99999999 } } // When the user presses Return with nothing selected, print a message Rectangle { id: notFoundMessage z: 999999999 visible: false anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter anchors.bottomMargin: 20 radius: 5 width: parent.width * 0.80 height: errorLabel.implicitHeight + 20 color: Kirigami.Theme.textColor opacity: 0 Behavior on opacity { NumberAnimation {duration: 150; easing.type: Easing.OutQuad} } Timer { id: errorMessageTimer running: false interval: 3000 repeat: false onTriggered: { notFoundMessage.visible = false notFoundMessage.opacity = 0 } } Text { id: errorLabel width: parent.width y: 10 wrapMode: Text.WordWrap horizontalAlignment: Text.AlignHCenter color: inactivePalette.base text: i18n("Cannot select this person because it was not found") } } onDisplayNotFoundMessage: { errorMessageTimer.running = true notFoundMessage.visible = true notFoundMessage.opacity = 1 } CheckBox { z: 199999999 anchors.bottom: parent.bottom anchors.right: parent.right anchors.margins: 10 text: i18n("Hide and do not show again") checked: false checkable: true visible: displayTips.showFirstTip && searchStateGroup.state == "firstSearch" onCheckStateChanged: { searchBox.searchFocus = false displayTips.showFirstTip = false } } // Search StateGroup { id: searchStateGroup states: [ State { name: "" when: searchState.inactiveState PropertyChanges { target: burryOverlay visible: false opacity: 0 } }, State { name: "searchActive" when: searchState.activeState PropertyChanges { target: burryOverlay visible: true opacity: 1 } }, State { name: "firstSearch" extend: "searchActive" when: searchState.firstSearchState } ] transitions: [ Transition { to: "searchActive" NumberAnimation { properties: "opacity" easing.type: Easing.InQuad duration: 200 loops: 1 } NumberAnimation { properties: "width,height" easing.type: Easing.InQuad duration: 0 loops: 1 } } ] } } diff --git a/src/jamisearch/qml/toolbar.qml b/src/jamisearch/qml/toolbar.qml index f2e7029b..14c13e26 100644 --- a/src/jamisearch/qml/toolbar.qml +++ b/src/jamisearch/qml/toolbar.qml @@ -1,55 +1,49 @@ /*************************************************************************** * Copyright (C) 2018 by Bluesystems * * Author : Emmanuel Lepage Vallee * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * **************************************************************************/ import QtQuick 2.7 import org.kde.kirigami 2.0 as Kirigami import org.kde.ringkde.genericutils 1.0 as GenericUtils import QtQuick.Layouts 1.0 as Layouts -Layouts.RowLayout { - anchors.right: parent.right - anchors.top: parent.top +Item { + implicitHeight: close.height - Behavior on x { - NumberAnimation {duration: 350; easing.type: Easing.OutQuad} - } - - Item { - Layouts.Layout.fillWidth: true - } - - GenericUtils.OutlineButton { + //TODO + /*GenericUtils.OutlineButton { label: " "+i18n("Scan a QR Code") visible: false //Not implemented height: 24 alignment: Qt.AlignRight - Layouts.Layout.maximumWidth: width + Layouts.Layout.maximumWidth: implicitWidth icon: "image://SymbolicColorizer/:/sharedassets/outline/qrcode.svg" - } + }*/ GenericUtils.OutlineButton { + id: close + label: " "+i18n("Close") height: 24 alignment: Qt.AlignRight icon: "image://SymbolicColorizer/:/sharedassets/outline/close.svg" - onClicked: { - hide() - } - } - Component.onCompleted: x = 0 + anchors.right: parent.right + anchors.top: parent.top + + onClicked: hide() + } }