diff --git a/assets/assets.qrc b/assets/assets.qrc index ebd3df5..6e5b4ed 100644 --- a/assets/assets.qrc +++ b/assets/assets.qrc @@ -1,11 +1,12 @@ Type.png Spaceship.png Grow.png BugSearch.png Bomb.png E-reading.png Astronaut.png + Faq.png diff --git a/main.qml b/main.qml index c70c1cf..c1097b8 100644 --- a/main.qml +++ b/main.qml @@ -1,185 +1,188 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import org.kde.kirigami 2.0 as Kirigami import org.kde.maui 1.0 as Maui import "src/widgets" import "src/views/notes" import "src/views/links" import "src/views/books" Maui.ApplicationWindow { id: root title: qsTr("Buho") /***** PROPS *****/ floatingBar: true - accentColor : "#8981d8" + footBarOverlap: true + accentColor : "#8981d8" + menuDrawer.bannerImageSource: "qrc:/Faq.png" property int currentView : views.notes property var views : ({ notes: 0, links: 1, books: 2, tags: 3, search: 4 }) headBar.middleContent: [ Maui.ToolButton { display: root.isWide ? ToolButton.TextBesideIcon : ToolButton.IconOnly onClicked: currentView = views.notes iconColor: currentView === views.notes? highlightColor : textColor iconName: "draw-text" text: qsTr("Notes") }, Maui.ToolButton { display: root.isWide ? ToolButton.TextBesideIcon : ToolButton.IconOnly onClicked: currentView = views.links iconColor: currentView === views.links? highlightColor : textColor iconName: "link" text: qsTr("Links") }, Maui.ToolButton { display: root.isWide ? ToolButton.TextBesideIcon : ToolButton.IconOnly iconColor: currentView === views.books? highlightColor : textColor iconName: "document-new" text: qsTr("Books") }, Maui.ToolButton { display: root.isWide ? ToolButton.TextBesideIcon : ToolButton.IconOnly iconColor: currentView === views.tags? highlightColor : textColor iconName: "tag" text: qsTr("Tags") } ] + footBarMargins: space.huge footBarAligment: Qt.AlignRight footBar.middleContent: [ Maui.PieButton { id: addButton iconName: "list-add" iconColor: "white" model: ListModel { ListElement {iconName: "document-new"; mid: "page"} ListElement {iconName: "link"; mid: "link"} ListElement {iconName: "draw-text"; mid: "note"} } onItemClicked: { if(item.mid === "note") newNote() else if(item.mid === "link") newLink() } } ] /***** COMPONENTS *****/ Connections { target: owl onNoteInserted: notesView.append(note) onLinkInserted: linksView.append(link) } NewNoteDialog { id: newNoteDialog onNoteSaved: owl.insertNote(note) } NewNoteDialog { id: editNote onNoteSaved: { if(owl.updateNote(note)) notesView.cardsView.currentItem.update(note) } } - NewLinkDialog { id: newLinkDialog - onLinkSaved: owl.insertLink(note.link, note.title.trim(), note.preview, note.color, note.tags) + onLinkSaved: owl.insertLink(link) } /***** VIEWS *****/ SwipeView { id: swipeView anchors.fill: parent currentIndex: currentView onCurrentIndexChanged: currentView = currentIndex NotesView { id: notesView onNoteClicked: setNote(note) } LinksView { id: linksView onLinkClicked: previewLink(link) } BooksView { id: booksView } } Component.onCompleted: { notesView.populate() linksView.populate() } function newNote() { currentView = views.notes newNoteDialog.open() } function newLink() { currentView = views.links newLinkDialog.open() } function setNote(note) { var tags = owl.getNoteTags(note.id) note.tags = tags notesView.currentNote = note editNote.fill(note) } function previewLink(link) { var tags = owl.getLinkTags(link.link) link.tags = tags + linksView.previewer.show(link) } } diff --git a/qml.qrc b/qml.qrc index 8fffc77..3918fcd 100644 --- a/qml.qrc +++ b/qml.qrc @@ -1,19 +1,21 @@ main.qml src/db/script.sql src/widgets/NewNoteDialog.qml src/widgets/CardsView.qml src/views/notes/NotesView.qml src/widgets/CardDelegate.qml src/widgets/NewLinkDialog.qml src/views/links/LinksView.qml utils.js src/views/links/WebViewAndroid.qml src/views/links/WebViewLinux.qml src/views/links/Previewer.qml src/views/books/BooksView.qml src/widgets/ColorsBar.qml src/widgets/CardsList.qml + src/widgets/CardMenu.qml + src/utils/owl.js diff --git a/src/db/db.cpp b/src/db/db.cpp index 308947a..5fbaaa2 100644 --- a/src/db/db.cpp +++ b/src/db/db.cpp @@ -1,240 +1,241 @@ /*** Pix Copyright (C) 2018 Camilo Higuita This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. 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 . ***/ #include "db.h" #include #include #include #include DB::DB(QObject *parent) : QObject(parent) { QDir collectionDBPath_dir(OWL::CollectionDBPath); if (!collectionDBPath_dir.exists()) collectionDBPath_dir.mkpath("."); this->name = QUuid::createUuid().toString(); if(!OWL::fileExists(OWL::CollectionDBPath + OWL::DBName)) { this->openDB(this->name); qDebug()<<"Collection doesn't exists, trying to create it" << OWL::CollectionDBPath + OWL::DBName; this->prepareCollectionDB(); }else this->openDB(this->name); } DB::~DB() { this->m_db.close(); } void DB::openDB(const QString &name) { if(!QSqlDatabase::contains(name)) { this->m_db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), name); this->m_db.setDatabaseName(OWL::CollectionDBPath + OWL::DBName); } if (!this->m_db.isOpen()) { if(!this->m_db.open()) qDebug()<<"ERROR OPENING DB"<m_db.lastError().text()<getQuery("PRAGMA synchronous=OFF"); query.exec(); } void DB::prepareCollectionDB() const { QSqlQuery query(this->m_db); QFile file(":/src/db/script.sql"); if (!file.exists()) { QString log = QStringLiteral("Fatal error on build database. The file '"); log.append(file.fileName() + QStringLiteral("' for database and tables creation query cannot be not found!")); qDebug()<getQuery(queryStr); if (query.exec()) { if (query.next()) return true; }else qDebug()<m_db); return query; } bool DB::insert(const QString &tableName, const QVariantMap &insertData) { if (tableName.isEmpty()) { qDebug()<m_db); query.prepare(sqlQueryString); int k = 0; foreach (const QVariant &value, values) query.bindValue(k++, value); return query.exec(); } bool DB::update(const QString &tableName, const QVariantMap &updateData, const QVariantMap &where) { if (tableName.isEmpty()) { qDebug()<getQuery(sqlQueryString); QVariantList values = updateData.values(); int k = 0; foreach (const QVariant &value, values) query.bindValue(k++, value); qDebug()<getQuery(queryStr); return query.exec(); } -bool DB::remove(const QString &tableName, const OWL::DB &removeData) +bool DB::remove(const QString &tableName, const QVariantMap &removeData) { if (tableName.isEmpty()) { qDebug()< 1 && igetQuery(sqlQueryString).exec(); + auto query = this->getQuery(sqlQueryString); + + QVariantList values = removeData.values(); + + int k = 0; + foreach (const QVariant &value, values) + query.bindValue(k++, value); + + return query.exec(); } diff --git a/src/db/db.h b/src/db/db.h index e2d51a9..d380df4 100644 --- a/src/db/db.h +++ b/src/db/db.h @@ -1,73 +1,73 @@ /*** Pix Copyright (C) 2018 Camilo Higuita This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. 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 . ***/ #ifndef DB_H #define DB_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../utils/owl.h" class DB : public QObject { Q_OBJECT private: QString name; QSqlDatabase m_db; public: explicit DB(QObject *parent = nullptr); ~ DB(); void openDB(const QString &name); /*basic public actions*/ void prepareCollectionDB() const; /* utils*/ Q_INVOKABLE bool checkExistance(const QString &tableName, const QString &searchId, const QString &search); protected: QSqlQuery getQuery(const QString &queryTxt); bool insert(const QString &tableName, const QVariantMap &insertData); bool update(const QString &tableName, const QVariantMap &updateData, const QVariantMap &where); bool update(const QString &table, const QString &column, const QVariant &newValue, const QVariant &op, const QString &id); - bool remove(const QString &tableName, const OWL::DB &removeData); + bool remove(const QString &tableName, const QVariantMap &removeData); signals: public slots: }; #endif // DB_H diff --git a/src/db/dbactions.cpp b/src/db/dbactions.cpp index 2aaa018..7dd690d 100644 --- a/src/db/dbactions.cpp +++ b/src/db/dbactions.cpp @@ -1,198 +1,249 @@ /*** Buho Copyright (C) 2018 Camilo Higuita This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. 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 . ***/ #include "dbactions.h" #include #include #include #include #include "linker.h" DBActions::DBActions(QObject *parent) : DB(parent) { qDebug() << "Getting collectionDB info from: " << OWL::CollectionDBPath; qDebug()<< "Starting DBActions"; this->tag = Tagging::getInstance(OWL::App, OWL::version, "org.kde.buho", OWL::comment); } DBActions::~DBActions() {} OWL::DB_LIST DBActions::getDBData(const QString &queryTxt) { OWL::DB_LIST mapList; auto query = this->getQuery(queryTxt); if(query.exec()) { while(query.next()) { OWL::DB data; for(auto key : OWL::KEYMAP.keys()) if(query.record().indexOf(OWL::KEYMAP[key])>-1) data.insert(key, query.value(OWL::KEYMAP[key]).toString()); mapList<< data; } }else qDebug()<< query.lastError()<< query.lastQuery(); return mapList; } QVariantList DBActions::get(const QString &queryTxt) { QVariantList mapList; auto query = this->getQuery(queryTxt); if(query.exec()) { while(query.next()) { QVariantMap data; for(auto key : OWL::KEYMAP.keys()) if(query.record().indexOf(OWL::KEYMAP[key])>-1) data[OWL::KEYMAP[key]] = query.value(OWL::KEYMAP[key]).toString(); mapList<< data; } }else qDebug()<< query.lastError()<< query.lastQuery(); return mapList; } bool DBActions::insertNote(const QVariantMap ¬e) { qDebug()<<"TAGS"<< note[OWL::KEYMAP[OWL::KEY::TAG]].toStringList(); auto title = note[OWL::KEYMAP[OWL::KEY::TITLE]].toString(); auto body = note[OWL::KEYMAP[OWL::KEY::BODY]].toString(); auto color = note[OWL::KEYMAP[OWL::KEY::COLOR]].toString(); auto pin = note[OWL::KEYMAP[OWL::KEY::PIN]].toInt(); auto fav = note[OWL::KEYMAP[OWL::KEY::FAV]].toInt(); auto tags = note[OWL::KEYMAP[OWL::KEY::TAG]].toStringList(); auto id = QUuid::createUuid().toString(); QVariantMap note_map = { {OWL::KEYMAP[OWL::KEY::ID], id}, {OWL::KEYMAP[OWL::KEY::TITLE], title}, {OWL::KEYMAP[OWL::KEY::BODY], body}, {OWL::KEYMAP[OWL::KEY::COLOR], color}, {OWL::KEYMAP[OWL::KEY::PIN], pin}, {OWL::KEYMAP[OWL::KEY::FAV], fav}, - {OWL::KEYMAP[OWL::KEY::UPDATED], QDateTime::currentDateTime()}, - {OWL::KEYMAP[OWL::KEY::ADD_DATE], QDateTime::currentDateTime()} + {OWL::KEYMAP[OWL::KEY::UPDATED], QDateTime::currentDateTime().toString()}, + {OWL::KEYMAP[OWL::KEY::ADD_DATE], QDateTime::currentDateTime().toString()} }; if(this->insert(OWL::TABLEMAP[OWL::TABLE::NOTES], note_map)) { for(auto tg : tags) this->tag->tagAbstract(tg, OWL::TABLEMAP[OWL::TABLE::NOTES], id, color); this->noteInserted(note_map); return true; } return false; } bool DBActions::updateNote(const QVariantMap ¬e) { auto id = note[OWL::KEYMAP[OWL::KEY::ID]].toString(); auto title = note[OWL::KEYMAP[OWL::KEY::TITLE]].toString(); auto body = note[OWL::KEYMAP[OWL::KEY::BODY]].toString(); auto color = note[OWL::KEYMAP[OWL::KEY::COLOR]].toString(); auto pin = note[OWL::KEYMAP[OWL::KEY::PIN]].toInt(); auto fav = note[OWL::KEYMAP[OWL::KEY::FAV]].toInt(); auto tags = note[OWL::KEYMAP[OWL::KEY::TAG]].toStringList(); QVariantMap note_map = { {OWL::KEYMAP[OWL::KEY::TITLE], title}, {OWL::KEYMAP[OWL::KEY::BODY], body}, {OWL::KEYMAP[OWL::KEY::COLOR], color}, {OWL::KEYMAP[OWL::KEY::PIN], pin}, {OWL::KEYMAP[OWL::KEY::FAV], fav}, {OWL::KEYMAP[OWL::KEY::UPDATED], QDateTime::currentDateTime().toString()} }; for(auto tg : tags) this->tag->tagAbstract(tg, OWL::TABLEMAP[OWL::TABLE::NOTES], id, color); return this->update(OWL::TABLEMAP[OWL::TABLE::NOTES], note_map, {{OWL::KEYMAP[OWL::KEY::ID], id}} ); } +bool DBActions::removeNote(const QVariantMap ¬e) +{ + qDebug()<remove(OWL::TABLEMAP[OWL::TABLE::NOTES], note); +} + QVariantList DBActions::getNotes() { return this->get("select * from notes"); } QVariantList DBActions::getNoteTags(const QString &id) { return this->tag->getAbstractTags(OWL::TABLEMAP[OWL::TABLE::NOTES], id); } -bool DBActions::insertLink(const QString &link, const QString &title, const QString &preview, const QString &color, const QStringList &tags) +bool DBActions::insertLink(const QVariantMap &link) { + auto url = link[OWL::KEYMAP[OWL::KEY::LINK]].toString(); + auto color = link[OWL::KEYMAP[OWL::KEY::COLOR]].toString(); + auto pin = link[OWL::KEYMAP[OWL::KEY::PIN]].toInt(); + auto fav = link[OWL::KEYMAP[OWL::KEY::FAV]].toInt(); + auto tags = link[OWL::KEYMAP[OWL::KEY::TAG]].toStringList(); + auto preview = link[OWL::KEYMAP[OWL::KEY::PREVIEW]].toString(); + auto title = link[OWL::KEYMAP[OWL::KEY::TITLE]].toString(); + auto image_path = OWL::saveImage(Linker::getUrl(preview), OWL::LinksPath+QUuid::createUuid().toString()); QVariantMap link_map = { - {OWL::KEYMAP[OWL::KEY::LINK], link}, + {OWL::KEYMAP[OWL::KEY::LINK], url}, {OWL::KEYMAP[OWL::KEY::TITLE], title}, + {OWL::KEYMAP[OWL::KEY::PIN], pin}, + {OWL::KEYMAP[OWL::KEY::FAV], fav}, {OWL::KEYMAP[OWL::KEY::PREVIEW], image_path}, {OWL::KEYMAP[OWL::KEY::COLOR], color}, - {OWL::KEYMAP[OWL::KEY::ADD_DATE], QDateTime::currentDateTime()} + {OWL::KEYMAP[OWL::KEY::ADD_DATE], QDateTime::currentDateTime().toString()}, + {OWL::KEYMAP[OWL::KEY::UPDATED], QDateTime::currentDateTime().toString()} + }; if(this->insert(OWL::TABLEMAP[OWL::TABLE::LINKS], link_map)) { for(auto tg : tags) - this->tag->tagAbstract(tg, OWL::TABLEMAP[OWL::TABLE::LINKS], link, color); + this->tag->tagAbstract(tg, OWL::TABLEMAP[OWL::TABLE::LINKS], url, color); this->linkInserted(link_map); return true; } return false; } +bool DBActions::updateLink(const QVariantMap &link) +{ + auto url = link[OWL::KEYMAP[OWL::KEY::LINK]].toString(); + auto color = link[OWL::KEYMAP[OWL::KEY::COLOR]].toString(); + auto pin = link[OWL::KEYMAP[OWL::KEY::PIN]].toInt(); + auto fav = link[OWL::KEYMAP[OWL::KEY::FAV]].toInt(); + auto tags = link[OWL::KEYMAP[OWL::KEY::TAG]].toStringList(); + + QVariantMap link_map = + { + {OWL::KEYMAP[OWL::KEY::COLOR], color}, + {OWL::KEYMAP[OWL::KEY::PIN], pin}, + {OWL::KEYMAP[OWL::KEY::FAV], fav}, + {OWL::KEYMAP[OWL::KEY::UPDATED], QDateTime::currentDateTime().toString()}, + }; + + for(auto tg : tags) + this->tag->tagAbstract(tg, OWL::TABLEMAP[OWL::TABLE::LINKS], url, color); + + return this->update(OWL::TABLEMAP[OWL::TABLE::LINKS], link_map, {{OWL::KEYMAP[OWL::KEY::LINK], url}} ); + +} + +bool DBActions::removeLink(const QVariantMap &link) +{ + return this->remove(OWL::TABLEMAP[OWL::TABLE::LINKS], link); +} + QVariantList DBActions::getLinks() { return this->get("select * from links"); } QVariantList DBActions::getLinkTags(const QString &link) { return this->tag->getAbstractTags(OWL::TABLEMAP[OWL::TABLE::LINKS], link); } bool DBActions::execQuery(const QString &queryTxt) { auto query = this->getQuery(queryTxt); return query.exec(); } +void DBActions::removeAbtractTags(const QString &key, const QString &lot) +{ + +} + diff --git a/src/db/dbactions.h b/src/db/dbactions.h index 9eea1e5..e926fb1 100644 --- a/src/db/dbactions.h +++ b/src/db/dbactions.h @@ -1,57 +1,63 @@ /*** Buho Copyright (C) 2018 Camilo Higuita This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. 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 . ***/ #ifndef DBACTIONS_H #define DBACTIONS_H #include #include "db.h" #include "tagging.h" class DBActions : public DB { Q_OBJECT public: explicit DBActions(QObject *parent = nullptr); ~DBActions(); Q_INVOKABLE QVariantList get(const QString &queryTxt); /*main actions*/ Q_INVOKABLE bool insertNote(const QVariantMap ¬e); Q_INVOKABLE bool updateNote(const QVariantMap ¬e); + Q_INVOKABLE bool removeNote(const QVariantMap ¬e); + Q_INVOKABLE QVariantList getNotes(); Q_INVOKABLE QVariantList getNoteTags(const QString &id); - Q_INVOKABLE bool insertLink(const QString &link, const QString &title, const QString &preview, const QString &color = QString(), const QStringList &tags = QStringList()); + Q_INVOKABLE bool insertLink(const QVariantMap &link); + Q_INVOKABLE bool updateLink(const QVariantMap &link); + Q_INVOKABLE bool removeLink(const QVariantMap &link); Q_INVOKABLE QVariantList getLinks(); Q_INVOKABLE QVariantList getLinkTags(const QString &link); protected: OWL::DB_LIST getDBData(const QString &queryTxt); bool execQuery(const QString &queryTxt); Tagging *tag; + void removeAbtractTags(const QString &key, const QString &lot); + signals: void noteInserted(QVariantMap note); void linkInserted(QVariantMap link); }; #endif // DBACTIONS_H diff --git a/src/db/script.sql b/src/db/script.sql index 9e63ab3..f312514 100644 --- a/src/db/script.sql +++ b/src/db/script.sql @@ -1,27 +1,30 @@ CREATE TABLE IF NOT EXISTS NOTES ( id TEXT PRIMARY KEY, title TEXT, body TEXT, color TEXT, fav INT, pin INT, addDate DATE, updated DATE ); CREATE TABLE IF NOT EXISTS BOOKS ( url TEXT PRIMARY KEY, title TEXT NOT NULL, fav INTEGER NOT NULL, addDate DATE ); CREATE TABLE IF NOT EXISTS LINKS ( link TEXT PRIMARY KEY, url TEXT, title TEXT, preview TEXT, color TEXT, -addDate DATE +fav INT, +pin INT, +addDate DATE, +updated DATE ); diff --git a/src/utils/owl.js b/src/utils/owl.js index aaec4df..edf7ae7 100644 --- a/src/utils/owl.js +++ b/src/utils/owl.js @@ -1,4 +1,16 @@ function saveNote(title, body, color, tags) { return owl.insertNote(title, body, color, tags) } + +function removeNote(note) +{ + var map = {id: note.id } + return owl.removeNote(map) +} + +function removeLink(link) +{ + var map = {link: link.link } + return owl.removeLink(map) +} diff --git a/src/views/links/LinksView.qml b/src/views/links/LinksView.qml index cceff2d..8607f89 100644 --- a/src/views/links/LinksView.qml +++ b/src/views/links/LinksView.qml @@ -1,98 +1,109 @@ import QtQuick 2.9 import "../../widgets" import org.kde.maui 1.0 as Maui +import "../../utils/owl.js" as O Maui.Page { id: control property alias cardsView : cardsView property alias previewer : previewer property var currentLink : ({}) signal linkClicked(var link) headBarVisible: !cardsView.holder.visible margins: isMobile ? space.big : space.enormus headBarExit: false + headBarTitle : cardsView.count + " links" headBar.leftContent: [ Maui.ToolButton { iconName: cardsView.gridView ? "view-list-icons" : "view-list-details" onClicked: { cardsView.gridView = !cardsView.gridView cardsView.refresh() } }, Maui.ToolButton { iconName: "view-sort-ascending" }, Maui.ToolButton { iconName: "view-sort-descending" } ] headBar.rightContent: [ Maui.ToolButton { iconName: "tag-recents" }, Maui.ToolButton { iconName: "window-pin" }, Maui.ToolButton { iconName: "view-calendar-day" } ] - Previewer { id: previewer + onLinkSaved: if(owl.updateLink(link)) + cardsView.currentItem.update(link) } CardsView { id: cardsView anchors.fill: parent onItemClicked: linkClicked(cardsView.model.get(index)) holder.emoji: "qrc:/Astronaut.png" holder.isMask: false holder.title : "No Links!" holder.body: "Click here to save a new link" holder.emojiSize: iconSizes.huge + Connections { target: cardsView.holder onActionTriggered: newLink() } + + Connections + { + target: cardsView.menu + onDeleteClicked: if(O.removeLink(cardsView.model.get(cardsView.currentIndex))) + cardsView.model.remove(cardsView.currentIndex) + } } function populate() { var data = owl.getLinks() for(var i in data) { console.log("PREVIEW", data[i].preview) append(data[i]) } } function append(link) { cardsView.model.append(link) } } diff --git a/src/views/links/Previewer.qml b/src/views/links/Previewer.qml index 7b5646a..e9d3a13 100644 --- a/src/views/links/Previewer.qml +++ b/src/views/links/Previewer.qml @@ -1,115 +1,183 @@ import QtQuick 2.0 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.0 import org.kde.maui 1.0 as Maui - +import "../../widgets" Popup { parent: ApplicationWindow.overlay height: parent.height * 0.9 width: parent.width * (isMobile ? 0.9 : 0.7) x: (parent.width / 2) - (width / 2) y: (parent.height /2 ) - (height / 2) modal: true clip: true padding: isAndroid ? 2 : "undefined" property alias webView: webViewer.item + signal linkSaved(var link) + Maui.Page { anchors.fill: parent margins: 0 padding: 0 - onExit: close() - headBar.leftContent: Maui.ToolButton - { - - iconName: "document-share" - onClicked: isAndroid ? Maui.Android.shareLink(webView.url) : - shareDialog.show(webView.url) - } - - headBar.rightContent: [ + headBarExit: false + headBar.leftContent: [ Maui.ToolButton { - iconName: "entry-delete" + id: pinButton + iconName: "window-pin" + checkable: true + iconColor: checked ? highlightColor : textColor + // onClicked: checked = !checked }, Maui.ToolButton { iconName: "document-save" }, Maui.ToolButton { iconName: "view-fullscreen" onClicked: owl.openLink(webView.url) } ] + headBar.rightContent: ColorsBar + { + id: colorBar + } + + footBar.leftContent: [ + + Maui.ToolButton + { + id: favButton + iconName: "love" + checkable: true + iconColor: checked ? "#ff007f" : textColor + }, + + Maui.ToolButton + { + iconName: "document-share" + onClicked: isAndroid ? Maui.Android.shareLink(webView.url) : + shareDialog.show(webView.url) + }, + + Maui.ToolButton + { + iconName: "document-export" + }, + + Maui.ToolButton + { + iconName: "entry-delete" + } + ] + + + footBar.rightContent: Row + { + spacing: space.medium + + Button + { + id: discard + text: qsTr("Discard") + onClicked: close() + + } + Button + { + id: save + text: qsTr("Save") + onClicked: + { + packLink() + close() + } + } + + } ColumnLayout { anchors.fill: parent -// Item -// { -// Layout.fillWidth: true -// height: rowHeightAlt - -// Label -// { -// clip: true -// text: webView.title -// width: parent.width -// height: parent.height -// horizontalAlignment: Qt.AlignHCenter -// verticalAlignment: Qt.AlignVCenter -// font.bold: true -// font.pointSize: fontSizes.big -// font.weight: Font.Bold -// elide: Label.ElideRight -// } -// } + // Item + // { + // Layout.fillWidth: true + // height: rowHeightAlt + + // Label + // { + // clip: true + // text: webView.title + // width: parent.width + // height: parent.height + // horizontalAlignment: Qt.AlignHCenter + // verticalAlignment: Qt.AlignVCenter + // font.bold: true + // font.pointSize: fontSizes.big + // font.weight: Font.Bold + // elide: Label.ElideRight + // } + // } Loader { id: webViewer Layout.fillWidth: true Layout.fillHeight: true Layout.margins: 0 source: isAndroid ? "qrc:/src/views/links/WebViewAndroid.qml" : "qrc:/src/views/links/WebViewLinux.qml" onVisibleChanged: { if(!visible) webView.url = "about:blank" } } Maui.TagsBar { id: tagBar Layout.fillWidth: true allowEditMode: true onTagsEdited: { for(var i in tags) append({tag : tags[i]}) } } } } function show(link) { webView.url = link.link tagBar.populate(link.tags) + pinButton.checked = link.pin == 1 + favButton.checked = link.fav == 1 open() } + + function packLink() + { + linkSaved({ + link: webView.url, + color: colorBar.currentColor, + tag: tagBar.getTags(), + pin: pinButton.checked, + fav: favButton.checked + }) + } } diff --git a/src/views/links/WebViewAndroid.qml b/src/views/links/WebViewAndroid.qml index 3da7e4f..c6bf2ae 100644 --- a/src/views/links/WebViewAndroid.qml +++ b/src/views/links/WebViewAndroid.qml @@ -1,14 +1,13 @@ import QtQuick 2.0 import QtWebView 1.1 WebView { id: webView - clip: true onLoadingChanged: { if (loadRequest.errorString) console.error(loadRequest.errorString); } } diff --git a/src/views/notes/NotesView.qml b/src/views/notes/NotesView.qml index c7594d8..3368bbd 100644 --- a/src/views/notes/NotesView.qml +++ b/src/views/notes/NotesView.qml @@ -1,121 +1,132 @@ import QtQuick 2.9 import QtQuick.Layouts 1.3 import "../../widgets" import org.kde.maui 1.0 as Maui import org.kde.kirigami 2.2 as Kirigami +import "../../utils/owl.js" as O Maui.Page { property alias cardsView : cardsView property var currentNote : ({}) signal noteClicked(var note) margins: 0 headBarExit : false headBarVisible: !cardsView.holder.visible + headBarTitle : cardsView.count + " notes" headBar.leftContent: [ Maui.ToolButton { iconName: cardsView.gridView ? "view-list-icons" : "view-list-details" onClicked: { cardsView.gridView = !cardsView.gridView cardsView.refresh() } }, Maui.ToolButton { iconName: "view-sort-ascending" }, Maui.ToolButton { iconName: "view-sort-descending" } ] headBar.rightContent: [ Maui.ToolButton { iconName: "tag-recents" }, Maui.ToolButton { id: pinButton iconName: "window-pin" checkable: true iconColor: checked ? highlightColor : textColor }, Maui.ToolButton { iconName: "view-calendar-day" } ] ColumnLayout { anchors.fill: parent spacing: 0 + CardsList { id: pinnedList visible: pinButton.checked Layout.margins: isMobile ? space.big : space.enormus Layout.alignment: Qt.AlignVCenter Layout.fillWidth: true height: cardsView.itemHeight itemHeight: cardsView.itemHeight * 0.9 itemWidth: itemHeight onItemClicked: noteClicked(cardsView.model.get(index)) } Kirigami.Separator { visible: pinnedList.visible Layout.fillWidth: true height: unit } CardsView { id: cardsView Layout.fillHeight: true Layout.fillWidth: true + width: parent.width Layout.margins: isMobile ? space.big : space.enormus onItemClicked: noteClicked(cardsView.model.get(index)) holder.emoji: "qrc:/Type.png" holder.emojiSize: iconSizes.huge holder.isMask: false holder.title : "No notes!" holder.body: "Click here to create a new note" Connections { target: cardsView.holder onActionTriggered: newNote() } + + Connections + { + target: cardsView.menu + onDeleteClicked: if(O.removeNote(cardsView.model.get(cardsView.currentIndex))) + cardsView.model.remove(cardsView.currentIndex) + } } } function populate() { var data = owl.getNotes() for(var i in data) append(data[i]) } function append(note) { cardsView.model.append(note) } } diff --git a/src/widgets/CardDelegate.qml b/src/widgets/CardDelegate.qml index affea08..bf528aa 100644 --- a/src/widgets/CardDelegate.qml +++ b/src/widgets/CardDelegate.qml @@ -1,171 +1,184 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.2 as Kirigami ItemDelegate { id: control property string noteColor : color ? color : viewBackgroundColor property int cardWidth: visible ? unit * 200 : 0 property int cardHeight: visible ? unit * 120 : 0 property int cardRadius: unit * 4 property bool condition : true + signal rightClicked(); + + visible: condition + width: cardWidth height: cardHeight hoverEnabled: !isMobile background: Rectangle { color: "transparent" } - visible: condition + MouseArea + { + anchors.fill: parent + acceptedButtons: Qt.RightButton + onClicked: + { + if(!isMobile && mouse.button === Qt.RightButton) + rightClicked() + } + } DropShadow { anchors.fill: card visible: card.visible horizontalOffset: 0 verticalOffset: 3 radius: 8.0 samples: 17 color: Qt.darker(noteColor, 1.5) source: card } Rectangle { id: card z: -999 anchors.centerIn: control anchors.fill: control border.color: Qt.darker(noteColor, 1.2) color: noteColor radius: cardRadius } Rectangle { anchors.fill: parent color: hovered? "#333" : "transparent" z: 999 opacity: 0.3 radius: cardRadius } ColumnLayout { height: parent.height * 0.9 width: parent.width * 0.9 anchors.centerIn: parent spacing: 0 clip: true Label { id: title - + padding: 0 visible: title.text.length > 0 Layout.leftMargin: space.medium Layout.topMargin: space.medium Layout.rightMargin: space.medium Layout.alignment: Qt.AlignLeft Layout.fillWidth: true text: model.title color: Qt.darker(model.color, 3) elide: Qt.ElideRight font.weight: Font.Bold font.bold: true font.pointSize: fontSizes.large } TextArea { id: body padding: 0 visible: typeof model.body !== 'undefined' Layout.leftMargin: visible ? space.medium : 0 Layout.bottomMargin: visible ? space.medium : 0 Layout.rightMargin: visible ? space.medium : 0 Layout.topMargin: title.visible ? 0 : space.medium Layout.alignment: Qt.AlignLeft Layout.fillHeight: visible Layout.fillWidth: visible enabled: false text: model.body color: Qt.darker(model.color, 3) textFormat: TextEdit.RichText font.pointSize: fontSizes.big background: Rectangle { color: "transparent" } } Item { id: preview Layout.fillHeight: true Layout.fillWidth: true Layout.margins: unit clip: true Layout.topMargin: space.medium visible: img.status === Image.Ready Image { id: img visible: status === Image.Ready asynchronous: true anchors.centerIn: parent horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter height: parent.height width: parent.width sourceSize.height: height sourceSize.width: width fillMode: Image.PreserveAspectCrop source: "file://"+encodeURIComponent( model.preview ) || "" layer.enabled: img.visible layer.effect: OpacityMask { maskSource: Item { width: img.width height: img.height Rectangle { anchors.centerIn: parent width: img.width height: img.height radius: cardRadius // radius: Math.min(width, height) } } } } } } - function update(note) + function update(item) { - console.log("UPDATE NOTES", note.pin) - model.title = note.title - model.body = note.body - model.color = note.color - model.pin = note.pin ? 1 : 0 - model.fav = note.fav ? 1 : 0 + console.log("update link color", item.color) + model.title = item.title + model.body = item.body + model.color = item.color + model.pin = item.pin ? 1 : 0 + model.fav = item.fav ? 1 : 0 } } diff --git a/src/widgets/CardMenu.qml b/src/widgets/CardMenu.qml new file mode 100644 index 0000000..02597f2 --- /dev/null +++ b/src/widgets/CardMenu.qml @@ -0,0 +1,87 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.3 + +Menu +{ + x: parent.width / 2 - width / 2 + y: parent.height / 2 - height / 2 + modal: true + focus: true + parent: ApplicationWindow.overlay + + margins: 1 + padding: 2 + + signal deleteClicked() + signal colorClicked() + + MenuItem + { + text: qsTr("Fav") + onTriggered: + { + close() + } + } + + MenuItem + { + text: qsTr("Pin") + onTriggered: + { + close() + } + } + + MenuItem + { + text: qsTr("Share") + onTriggered: + { + close() + } + } + + MenuItem + { + text: qsTr("Edit") + onTriggered: + { + close() + } + } + + MenuItem + { + text: qsTr("Export") + onTriggered: + { + close() + } + } + + MenuItem + { + text: qsTr("Delete") + onTriggered: + { + deleteClicked() + close() + } + } + + MenuItem + { + width: parent.width + + ColorsBar + { + width: parent.width + size: iconSizes.small + onColorPicked: colorClicked(color) + } + } + + + +} diff --git a/src/widgets/CardsView.qml b/src/widgets/CardsView.qml index 5551b6c..16ce65b 100644 --- a/src/widgets/CardsView.qml +++ b/src/widgets/CardsView.qml @@ -1,64 +1,82 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import org.kde.kirigami 2.2 as Kirigami import org.kde.maui 1.0 as Maui GridView { property bool gridView : true property alias holder : holder + property alias menu : cardMenu readonly property int defaultSize : unit * 200 - property int itemWidth : !gridView ? parent.width * 0.9 : + property int itemWidth : !gridView ? width : isMobile? (width-itemSpacing) * 0.42 : unit * 200 property int itemHeight: unit * 120 property int itemSpacing: space.huge signal itemClicked(int index) boundsBehavior: !isMobile? Flickable.StopAtBounds : Flickable.DragAndOvershootBounds cellWidth: itemWidth + itemSpacing cellHeight: itemHeight + itemSpacing clip : true Maui.Holder { id: holder - visible: count < 1 + visible: count < 1 + } + + CardMenu + { + id: cardMenu } model: ListModel { id: cardsModel} delegate: CardDelegate { id: delegate cardWidth: itemWidth cardHeight: itemHeight onClicked: { currentIndex = index itemClicked(index) } + + onRightClicked: + { + currentIndex = index + cardMenu.popup() + } + + onPressAndHold: + { + currentIndex = index + cardMenu.popup() + } } // onWidthChanged: if(!isMobile && gridView) adaptGrid() function adaptGrid() { var amount = parseInt(width/(itemWidth + itemSpacing),10) var leftSpace = parseInt(width-(amount*(itemWidth + itemSpacing)), 10) var size = parseInt((itemWidth + itemSpacing)+(parseInt(leftSpace/amount, 10)), 10) size = size > itemWidth + itemSpacing ? size : itemWidth + itemSpacing cellWidth = size } function refresh() { model = cardsModel } } diff --git a/src/widgets/ColorsBar.qml b/src/widgets/ColorsBar.qml index f168760..0cf707b 100644 --- a/src/widgets/ColorsBar.qml +++ b/src/widgets/ColorsBar.qml @@ -1,105 +1,131 @@ import QtQuick 2.0 import QtQuick.Controls 2.2 Row { signal colorPicked(color color) anchors.verticalCenter: parent.verticalCenter spacing: space.medium + property string currentColor + property int size : iconSizes.medium Rectangle { color:"#ffded4" anchors.verticalCenter: parent.verticalCenter - height: iconSizes.medium + height: size width: height radius: Math.max(height, width) - border.color: borderColor + border.color: Qt.darker(color, 1.7) MouseArea { anchors.fill: parent - onClicked: colorPicked(parent.color) + onClicked: + { + currentColor = parent.color + colorPicked(currentColor) + } } } Rectangle { color:"#d3ffda" anchors.verticalCenter: parent.verticalCenter - height: iconSizes.medium + height: size width: height radius: Math.max(height, width) - border.color: borderColor + border.color: Qt.darker(color, 1.7) MouseArea { anchors.fill: parent - onClicked: colorPicked(parent.color) + onClicked: + { + currentColor = parent.color + colorPicked(currentColor) + } } } Rectangle { color:"#caf3ff" anchors.verticalCenter: parent.verticalCenter - height: iconSizes.medium + height: size width: height radius: Math.max(height, width) - border.color: borderColor + border.color: Qt.darker(color, 1.7) MouseArea { anchors.fill: parent - onClicked: colorPicked(parent.color) + onClicked: + { + currentColor = parent.color + colorPicked(currentColor) + } } } Rectangle { color:"#dbd8ff" anchors.verticalCenter: parent.verticalCenter - height: iconSizes.medium + height: size width: height radius: Math.max(height, width) - border.color: borderColor + border.color: Qt.darker(color, 1.7) MouseArea { anchors.fill: parent - onClicked: colorPicked(parent.color) + onClicked: + { + currentColor = parent.color + colorPicked(currentColor) + } } } Rectangle { color:"#ffcdf4" anchors.verticalCenter: parent.verticalCenter - height: iconSizes.medium + height: size width: height radius: Math.max(height, width) - border.color: borderColor + border.color: Qt.darker(color, 1.7) MouseArea { anchors.fill: parent - onClicked: colorPicked(parent.color) + onClicked: + { + currentColor = parent.color + colorPicked(currentColor) + } } } Rectangle { color: viewBackgroundColor anchors.verticalCenter: parent.verticalCenter - height: iconSizes.medium + height: size width: height radius: Math.max(height, width) - border.color: borderColor + border.color: Qt.darker(color, 1.7) MouseArea { anchors.fill: parent - onClicked: colorPicked(parent.color) + onClicked: + { + currentColor = parent.color + colorPicked(currentColor) + } } } } diff --git a/src/widgets/NewLinkDialog.qml b/src/widgets/NewLinkDialog.qml index bb826ed..1161b00 100644 --- a/src/widgets/NewLinkDialog.qml +++ b/src/widgets/NewLinkDialog.qml @@ -1,219 +1,262 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.0 import org.kde.maui 1.0 as Maui import org.buho.editor 1.0 import org.kde.kirigami 2.2 as Kirigami Popup { parent: ApplicationWindow.overlay height: previewReady ? parent.height * (isMobile ? 0.8 : 0.7) : contentLayout.implicitHeight width: parent.width * (isMobile ? 0.9 : 0.7) - signal linkSaved(var note) + signal linkSaved(var link) property string selectedColor : "#ffffe6" property string fgColor: Qt.darker(selectedColor, 2.5) property bool previewReady : false x: (parent.width / 2) - (width / 2) y: (parent.height /2 ) - (height / 2) modal: true padding: isAndroid ? 1 : "undefined" Connections { target: linker onPreviewReady: { previewReady = true fill(link) } } Maui.Page { id: content margins: 0 anchors.fill: parent Rectangle { id: bg color: selectedColor z: -1 anchors.fill: parent } - onExit: clear() + headBarExit: false headBarVisible: previewReady footBarVisible: previewReady + + headBar.leftContent: Maui.ToolButton + { + id: pinButton + iconName: "window-pin" + checkable: true + iconColor: checked ? highlightColor : textColor + // onClicked: checked = !checked + } + headBar.rightContent: ColorsBar { onColorPicked: selectedColor = color } ColumnLayout { id: contentLayout anchors.fill: parent TextField { id: link Layout.fillWidth: true Layout.margins: space.medium height: rowHeight verticalAlignment: Qt.AlignVCenter placeholderText: qsTr("URL") font.weight: Font.Bold font.bold: true font.pointSize: fontSizes.large color: fgColor Layout.alignment: Qt.AlignCenter background: Rectangle { color: "transparent" } onAccepted: linker.extract(link.text) } TextField { id: title visible: previewReady Layout.fillWidth: true Layout.margins: space.medium height: 24 placeholderText: qsTr("Title") font.weight: Font.Bold font.bold: true font.pointSize: fontSizes.large color: fgColor background: Rectangle { color: "transparent" } } Item { Layout.fillWidth: true Layout.fillHeight: true visible: previewReady ListView { id: previewList anchors.fill: parent anchors.centerIn: parent clip: true snapMode: ListView.SnapOneItem orientation: ListView.Horizontal interactive: count > 1 highlightFollowsCurrentItem: true model: ListModel{} onMovementEnded: { var index = indexAt(contentX, contentY) currentIndex = index } delegate: ItemDelegate { height: previewList.height width: previewList.width background: Rectangle { color: "transparent" } Image { id: img source: model.url fillMode: Image.PreserveAspectFit asynchronous: true width: parent.width height: parent.height sourceSize.height: height horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter } } } } Maui.TagsBar { id: tagBar visible: previewReady Layout.fillWidth: true allowEditMode: true onTagsEdited: { for(var i in tags) append({tag : tags[i]}) } } } + footBar.leftContent: [ - footBar.rightContent: [ - Button + Maui.ToolButton { - id: save - text: qsTr("Save") - onClicked: - { - var data = ({ - link : link.text, - title: title.text, - preview: previewList.model.get(previewList.currentIndex).url, - color: selectedColor, - tags: tagBar.getTags() - }) - linkSaved(data) - clear() - } + id: favButton + iconName: "love" + checkable: true + iconColor: checked ? "#ff007f" : textColor + }, + + Maui.ToolButton + { + iconName: "document-share" + onClicked: isAndroid ? Maui.Android.shareText(link.text) : + shareDialog.show(link.text) }, + Maui.ToolButton + { + iconName: "document-export" + } + ] + + footBar.rightContent: Row + { + spacing: space.medium + Button { id: discard text: qsTr("Discard") onClicked: clear() } - ] + + Button + { + id: save + text: qsTr("Save") + onClicked: + { + packLink() + clear() + } + } + } } function clear() { title.clear() link.clear() previewList.model.clear() + tagBar.clear() previewReady = false close() } function fill(note) { title.text = note.title[0] populatePreviews(note.image) open() } function populatePreviews(imgs) { for(var i in imgs) previewList.model.append({url : imgs[i]}) } + + function packLink() + { + var data = ({ + link : link.text, + title: title.text.trim(), + preview: previewList.model.get(previewList.currentIndex).url, + color: selectedColor, + tag: tagBar.getTags(), + pin: pinButton.checked, + fav: favButton.checked + }) + linkSaved(data) + } } diff --git a/src/widgets/NewNoteDialog.qml b/src/widgets/NewNoteDialog.qml index c46ec2d..046eff5 100644 --- a/src/widgets/NewNoteDialog.qml +++ b/src/widgets/NewNoteDialog.qml @@ -1,265 +1,264 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.0 import org.kde.maui 1.0 as Maui import org.buho.editor 1.0 Popup { parent: ApplicationWindow.overlay height: parent.height * (isMobile ? 0.8 : 0.7) width: parent.width * (isMobile ? 0.9 : 0.7) property string selectedColor : "#ffffe6" property string fgColor: Qt.darker(selectedColor, 2.5) property bool showEditActions : false signal noteSaved(var note) x: (parent.width / 2) - (width / 2) y: (parent.height /2 ) - (height / 2) modal: true padding: isAndroid ? 1 : "undefined" Maui.Page { id: content anchors.fill: parent margins: 0 onExit: clear() - + headBarExit: false Rectangle { id: bg color: selectedColor z: -1 anchors.fill: parent } headBar.leftContent: [ + Maui.ToolButton + { + id: pinButton + iconName: "window-pin" + checkable: true + iconColor: checked ? highlightColor : textColor + // onClicked: checked = !checked + }, + Maui.ToolButton { iconName: "format-text-bold" focusPolicy: Qt.TabFocus iconColor: checked ? highlightColor : textColor checkable: true checked: document.bold onClicked: document.bold = !document.bold }, Maui.ToolButton { iconName: "format-text-italic-symbolic" iconColor: checked ? highlightColor : textColor focusPolicy: Qt.TabFocus checkable: true checked: document.italic onClicked: document.italic = !document.italic }, Maui.ToolButton { iconName: "format-text-underline-symbolic" }, Maui.ToolButton { iconName: "format-text-uppercase" }, Maui.ToolButton { iconName: "image" } ] - headBar.rightContent: Row - { - spacing: space.medium + headBar.rightContent: ColorsBar - { - onColorPicked: selectedColor = color - } - - Maui.ToolButton - { - id: pinButton - iconName: "window-pin" - checkable: true - iconColor: checked ? highlightColor : textColor -// onClicked: checked = !checked - } + { + onColorPicked: selectedColor = color } + ColumnLayout { anchors.fill: parent TextField { id: title Layout.fillWidth: true Layout.margins: space.medium height: 24 placeholderText: qsTr("Title") font.weight: Font.Bold font.bold: true font.pointSize: fontSizes.large color: fgColor background: Rectangle { color: "transparent" } } DocumentHandler { id: document document: body.textDocument cursorPosition: body.cursorPosition selectionStart: body.selectionStart selectionEnd: body.selectionEnd // textColor: TODO // onLoaded: { // body.text = text // } onError: { body.text = message body.visible = true } } ScrollView { Layout.fillHeight: true Layout.fillWidth: true Layout.margins: space.medium TextArea { id: body width: parent.width height: parent.height placeholderText: qsTr("Body") selectByKeyboard :!isMobile selectByMouse : !isMobile textFormat : TextEdit.AutoText color: fgColor font.pointSize: fontSizes.large wrapMode: TextEdit.WrapAnywhere background: Rectangle { color: "transparent" } } } Maui.TagsBar { id: tagBar Layout.fillWidth: true allowEditMode: true onTagsEdited: { for(var i in tags) append({tag : tags[i]}) } } } footBar.leftContent: [ Maui.ToolButton { id: favButton iconName: "love" checkable: true iconColor: checked ? "#ff007f" : textColor }, Maui.ToolButton { iconName: "document-share" onClicked: isAndroid ? Maui.Android.shareText(body.text) : shareDialog.show(body.text) }, Maui.ToolButton { iconName: "document-export" }, Maui.ToolButton { iconName: "entry-delete" } ] - footBar.rightContent: [ + footBar.rightContent: Row + { + spacing: space.medium + + Button + { + id: discard + text: qsTr("Discard") + onClicked: clear() + + } Button { id: save text: qsTr("Save") onClicked: { if(body.text.length > 0) packNote() clear() } - }, - - Button - { - id: discard - text: qsTr("Discard") - onClicked: clear() - } - - ] + } } onOpened: body.forceActiveFocus() function clear() { title.clear() body.clear() close() } function fill(note) { title.text = note.title body.text = note.body selectedColor = note.color pinButton.checked = note.pin == 1 favButton.checked = note.fav == 1 tagBar.populate(note.tags) open() } function packNote() { noteSaved({ id: notesView.currentNote.id, title: title.text.trim(), body: body.text, color: selectedColor, tag: tagBar.getTags(), pin: pinButton.checked, fav: favButton.checked }) } }