diff --git a/src/contactview/assets/welcome.html b/src/contactview/assets/welcome.html index 858bf0ae..4c836877 100644 --- a/src/contactview/assets/welcome.html +++ b/src/contactview/assets/welcome.html @@ -1,24 +1,18 @@
 

Welcome to Ring-KDE

- Ring-KDE is build entirely using standard compliant technologies. All of - your data never leave your computer and is readable using various third - party application. If you wish to keep your account, don't forget to - export it somewhere safe. If you format your computer without having the - account on another device, it will be lost forever + Above is the search box. To begin to use Ring-KDE, you need to add someone + to talk too. Since no contacts have been imported yet, use the following + search term to locate someone:

- Below is the search box. To begin to use Ring-KDE, you need to add someone - to talk too. Since no contacts have been imported yet, use the following - search term to locate someone:


-

diff --git a/src/jamisearch/qml/searchbox.qml b/src/jamisearch/qml/searchbox.qml index 225bf763..6cf07e4d 100644 --- a/src/jamisearch/qml/searchbox.qml +++ b/src/jamisearch/qml/searchbox.qml @@ -1,173 +1,181 @@ /*************************************************************************** * 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 alias searchFocus: search.focus property alias labelWidth: findLabel.implicitWidth 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 + + // 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 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 { height: textMetric.height*1.5 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) { - displayNotFoundMessage() + searchView.displayNotFoundMessage() return } if (!cm) return cm = RingSession.individualDirectory.fromTemporary(cm) mainPage.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 cebf3a80..26e51f77 100644 --- a/src/jamisearch/qml/searchoverlay.qml +++ b/src/jamisearch/qml/searchoverlay.qml @@ -1,314 +1,285 @@ /*************************************************************************** * 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 alias topLevel: seachOverlay.parent property var source: null - property bool active: searchBox && searchBox.searchFocus - property alias currentIndex: searchView.currentIndex - property alias currentItem: searchView.currentItem + property bool active: searchState.display property QtObject searchBox: null property bool displayWelcome: searchState.firstSearchState + property alias currentItem: searchView.currentItem // used be the search box + // 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.empty - focussed: active + 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 - (searchBox ? searchBox.labelWidth : 0) + width: parent.width anchors.right: parent.parent.right y: -searchBox.height 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.firstSearchState + 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 Layouts.Layout.fillWidth: true Layouts.Layout.preferredHeight: active ? height : 0 Layouts.Layout.maximumHeight: active ? height : 0 } // 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 - ParentChange { - target: seachOverlay - parent: topLevel - } - - PropertyChanges { - target: seachOverlay - anchors.fill: parent - } - PropertyChanges { target: burryOverlay visible: false opacity: 0 } }, State { name: "searchActive" when: searchState.activeState -// PropertyChanges { -// target: seachOverlay -// anchors.fill: parent -// width: undefined -// height: undefined -// } - PropertyChanges { target: burryOverlay visible: true opacity: 1 } - - PropertyChanges { - target: effectSource - sourceRect: Qt.rect(0, 0, parent.width, parent.height) - } }, State { name: "firstSearch" extend: "searchActive" when: searchState.firstSearchState - -// PropertyChanges { -// target: seachOverlay -// anchors.fill: undefined -// width: applicationWindow().contentItem.width -// height: applicationWindow().contentItem.height -// } -// -// PropertyChanges { -// target: searchBox -// searchFocus: true -// } } ] 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/searchstate.qml b/src/jamisearch/qml/searchstate.qml index 8a39fd55..8e062f74 100644 --- a/src/jamisearch/qml/searchstate.qml +++ b/src/jamisearch/qml/searchstate.qml @@ -1,124 +1,126 @@ /*************************************************************************** * 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 /** * Avoids a spaghetti mess of multi line, multi state expressions to define * how to display the search popup. */ QtObject { // States property bool inactiveState: false property bool firstSearchState: false property bool activeState: false property string state: "inactive" + property bool display: _delayed && (firstSearchState || activeState) // Extra widgets property bool displaySearchHelp: false property bool displaySearchCategories: false + property bool displayWelcome: searchEmpty && firstSearchState // External status property bool searchEmpty: false property bool focussed: false // Geometry property var normalSearchArea: undefined property var firstSearchArea: undefined // Internal property bool _delayed: false property bool _hasSearched: false property var _c1: Connections { target: displayTips onShowFirstTipChanged: _evalState() } property var _c2: Connections { target: RingSession.peersTimelineModel onEmptyChanged: _evalState() } function _evalState() { // Do not evaluate during initialization as it wont have the // whole picture of what is and isn't set. if (!_delayed) return var isFirst = displayTips.showFirstTip && RingSession.peersTimelineModel.empty // State if (isFirst) { firstSearchState = true activeState = false inactiveState = false } else if (focussed) { firstSearchState = false activeState = true inactiveState = false } else { firstSearchState = false activeState = false inactiveState = true } // Extra widgets displaySearchHelp = activeState && searchEmpty displaySearchCategories = activeState && (!searchEmpty) var newState = "" if (firstSearchState) newState = "firstSearch" else if (activeState) newState = "active" else newState = "inactive" // Prevent too many signals if (newState != state) state = newState } onFocussedChanged: _evalState() onSearchEmptyChanged: { _hasSearched = _hasSearched || !searchEmpty _evalState() } /** * QML properties are evaluated in a random order, this exists to stop * trying to handle everything. * * Keep in mind that some other components already have hacks to handle * this. So this has to be done /after/ those hacks * (which cannot be detected), hence the timer. */ property var _t: Timer { repeat: false running: true - interval: 0 + interval: 100 onTriggered: { _delayed = true _evalState() } } } diff --git a/views/basic/qml/basic.qml b/views/basic/qml/basic.qml index 207fd414..e7e7f20a 100644 --- a/views/basic/qml/basic.qml +++ b/views/basic/qml/basic.qml @@ -1,155 +1,158 @@ /* * Copyright 2018 Fabian Riethmayer * Copyright 2019 Emmanuel Lepage * * 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 3, 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.6 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.2 import org.kde.kirigami 2.6 as Kirigami import net.lvindustries.ringqtquick 1.0 as RingQtQuick import org.kde.ringkde.basicview 1.0 as BasicView import org.kde.ringkde.jamicontactview 1.0 as JamiContactView import org.kde.ringkde.jamiwizard 1.0 as JamiWizard import org.kde.ringkde.jamikdeintegration 1.0 as JamiKDEIntegration import QtQuick.Controls.Material 2.3 Kirigami.ApplicationWindow { width: 320 height: 600 id: root // Localization is currently broken with the Material theme function i18n(t) {return t;} RingQtQuick.SharedModelLocker { id: mainPage onChanged: { chat.boo.timelineModel = timelineModel if (!currentIndividual) return; var idx = RingSession.peersTimelineModel.individualIndex(currentIndividual) list.currentIndex = idx.row } } BasicView.ActionCollection { id: actionCollection } BasicView.Contacts { id : mydata Component.onCompleted: { chat.model = mydata.get(3) } } pageStack.initialPage: [list, chat] pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.ToolBar pageStack.globalToolBar.preferredHeight: Kirigami.Units.gridUnit * 3 pageStack.defaultColumnWidth: root.width < 320 ? root.width : 320 BasicView.ListPage { id: list model: RingSession.peersTimelineModel } BasicView.ChatPage { id: chat } contextDrawer: Kirigami.ContextDrawer { id: contextDrawer } globalDrawer: BasicView.GlobalDrawer { id: globalDrawer } JamiKDEIntegration.WindowEvent { id: events onRequestsConfigureAccounts: { var component = Qt.createComponent("qrc:/account/qml/accountdialog.qml") if (component.status == Component.Ready) { var window = component.createObject(applicationWindow().contentItem) window.open() } else console.log("ERROR", component.status, component.errorString()) } onRequestsHideWindow: { hide() } onRequestsWizard: { globalDrawer.drawerOpen = false wizardLoader.visible = true wizardLoader.active = true } } /** * Display the wizard when all accounts are deleted. */ JamiWizard.Policies { id: wizardPolicies } Loader { id: wizardLoader active: false anchors.fill: parent z: 999999 onActiveChanged: { if ( list.displayWelcome && !active) list.search() } sourceComponent: JamiWizard.Wizard { anchors.fill: parent z: 999999 onVisibleChanged: { if (!visible) { wizardLoader.visible = false wizardLoader.active = false } } onWizardFinished: { wizardLoader.active = false } } } Timer { interval: 0 running: true repeat: false onTriggered: { - wizardLoader.active = wizardPolicies.displayWizard + if (wizardPolicies.displayWizard) + wizardLoader.active = true + else if (list.displayWelcome) + list.search() } } } diff --git a/views/basic/qml/listpage.qml b/views/basic/qml/listpage.qml index e89d791d..afbf1b78 100644 --- a/views/basic/qml/listpage.qml +++ b/views/basic/qml/listpage.qml @@ -1,132 +1,141 @@ /* * Copyright 2018 Fabian Riethmayer * Copyright 2019 Emmanuel Lepage * * 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 3, 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.6 import QtQuick.Controls 2.2 as Controls import org.kde.kirigami 2.4 as Kirigami import QtQuick.Layouts 1.2 as Layouts import org.kde.ringkde.basicview 1.0 as BasicView import org.kde.ringkde.jamitroubleshooting 1.0 as JamiTroubleShooting import org.kde.ringkde.jamisearch 1.0 as JamiSearch Kirigami.Page { id: peerListPage property alias currentIndex: list.currentIndex; property alias model: list.model property bool displayWelcome: false spacing: 0 leftPadding: 0; rightPadding: 0; topPadding: 0;bottomPadding: 0; padding: 0 signal search() Kirigami.Theme.colorSet: Kirigami.Theme.View header: Layouts.ColumnLayout { visible: globalTroubleshoot.sourceComponent != null height: visible && globalTroubleshoot.active ? implicitHeight : 0 width: peerListPage.width spacing: Kirigami.Units.largeSpacing JamiTroubleShooting.GlobalTroubleshoot { id: globalTroubleshoot Layouts.Layout.fillWidth: true Layouts.Layout.margins: Kirigami.Units.largeSpacing } Item { visible: globalTroubleshoot.sourceComponent != null height: 10 } } title: i18n("Address book") background: Rectangle { color: Kirigami.Theme.backgroundColor } titleDelegate: Item { id: header implicitHeight: parent.parent.height - 2*Kirigami.Units.largeSpacing implicitWidth: 10 JamiSearch.SearchBox { id: headerSearchbox searchView: _searchView anchors.centerIn: parent anchors.margins: Kirigami.Units.largeSpacing width: parent.width - 2 * Kirigami.Units.largeSpacing height: headerSearchbox.focus ? parent.height : parent.height * 1.5 z: 9999 Connections { target: peerListPage onSearch: headerSearchbox.searchFocus = true } + + /*Connections { + target: peerListPage + onDisplayWelcomeChanged: { + if (peerListPage.displayWelcome) + headerSearchbox.searchFocus = true + } + }*/ + } JamiSearch.Overlay { id: _searchView source: peerListPage searchBox: headerSearchbox width: peerListPage.width height: peerListPage.height + header.height x: -(peerListPage.width - header.width) y: -Kirigami.Units.largeSpacing onDisplayWelcomeChanged: { peerListPage.displayWelcome = displayWelcome } onContactMethodSelected: { mainPage.currentContactMethod = cm pageStack.push(chat) var idx = RingSession.peersTimelineModel.individualIndex(cm.individual) list.currentIndex = idx.row } //HACK obey god dammit onHeightChanged: { height = peerListPage.height + header.height x = -(peerListPage.width - header.width) width = peerListPage.width } //HACK obey god dammit onActiveChanged: { if (!active) return width = peerListPage.width height = peerListPage.height + header.height x = -(peerListPage.width - header.width) } z: 9998 } } BasicView.List { id: list width: parent.width height: parent.height } }