mirror of
https://github.com/kiwix/kiwix-desktop.git
synced 2025-09-08 03:32:30 -04:00
Replace QWebEngineView with QTreeView for ContentManagerView
We'll be using a QTreeView to display elements of the library.
This commit is contained in:
parent
d9d06eaf71
commit
8f61b418f9
@ -1,8 +1,5 @@
|
|||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>js/vue.js</file>
|
|
||||||
<file>texts/_contentManager.html</file>
|
|
||||||
<file>css/_contentManager.css</file>
|
<file>css/_contentManager.css</file>
|
||||||
<file>js/_contentManager.js</file>
|
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
@ -1,271 +0,0 @@
|
|||||||
html, body {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
height: 100%;
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
#app {
|
|
||||||
height: 100%;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
*:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#searchBar {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#searchInput {
|
|
||||||
background-image: url('qrc:///icons/search.svg');
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: left 10px top 10px;
|
|
||||||
background-size: 23px 23px;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
padding-left: 45px;
|
|
||||||
height: 40px;
|
|
||||||
width: 90%;
|
|
||||||
border: 1px solid #EEE;
|
|
||||||
}
|
|
||||||
|
|
||||||
#bookTable {
|
|
||||||
position: relative;
|
|
||||||
height: calc(100% - 42px); /* 42px = 40px(height of #searchInput) + 2px(border) */
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#bookList {
|
|
||||||
height: calc(100% - 19px - 20px); /*19px the header size, 20px the header margin-top */
|
|
||||||
overflow-y:scroll;
|
|
||||||
overflow-x:hidden;
|
|
||||||
position: relative;
|
|
||||||
width: 100%
|
|
||||||
}
|
|
||||||
|
|
||||||
.tablerow,
|
|
||||||
.header {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.header {
|
|
||||||
color: #555;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
.tablecell{
|
|
||||||
flex-basis:20%;
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sortable:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sortableBold {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
|
||||||
border: solid black;
|
|
||||||
border-width: 0 3px 3px 0;
|
|
||||||
display: inline-block;
|
|
||||||
padding: 3px;
|
|
||||||
transform: rotate(-45deg);
|
|
||||||
-webkit-transform: rotate(-45deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.arrowUp {
|
|
||||||
transform: rotate(-135deg);
|
|
||||||
-webkit-transform: rotate(-135deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.arrowDown {
|
|
||||||
transform: rotate(45deg);
|
|
||||||
-webkit-transform: rotate(45deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cell0 {
|
|
||||||
flex-basis: 60px;
|
|
||||||
flex-grow: 0;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
.tablerow > .cell1 {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.cell0 > img {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
.cell1,
|
|
||||||
.cell2,
|
|
||||||
.cell3,
|
|
||||||
.cell4,
|
|
||||||
.cell5 {
|
|
||||||
flex-grow: 1;
|
|
||||||
margin: 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cell2,
|
|
||||||
.cell3,
|
|
||||||
.cell4 {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cell5 {
|
|
||||||
flex-basis: 30%;
|
|
||||||
flex-grow: 2;
|
|
||||||
flex-direction: row;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
summary {
|
|
||||||
height: 64px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.book {
|
|
||||||
border-top: 1px solid #EEE;
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
background: transparent;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
.tablerow button {
|
|
||||||
/* color: blue; */
|
|
||||||
color:#555;
|
|
||||||
/* font-weight: bold; */
|
|
||||||
font-size: 16px;
|
|
||||||
border: 0;
|
|
||||||
background: transparent;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
.tablerow button::first-letter {
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
.tablerow button:hover {
|
|
||||||
/* color: white;
|
|
||||||
background: blue; */
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
details:hover {
|
|
||||||
background-color: #d9e9ff;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu {
|
|
||||||
/* width: 120px; */
|
|
||||||
box-shadow: 0 4px 5px 3px rgba(0, 0, 0, 0.2);
|
|
||||||
position: fixed;
|
|
||||||
display: none;
|
|
||||||
z-index: 99999999999999;
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-options {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 2px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-option {
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 14px;
|
|
||||||
padding: 10px 40px 10px 20px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-option:hover {
|
|
||||||
background: rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
.loader {
|
|
||||||
margin: 0 auto;
|
|
||||||
border: 16px solid #f3f3f3; /* Light grey */
|
|
||||||
border-top: 16px solid #3498db; /* Blue */
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 120px;
|
|
||||||
height: 120px;
|
|
||||||
animation: spin 2s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.do-not-display {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
0% { transform: rotate(0deg); }
|
|
||||||
100% { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--circle-wrap-dimension: 40px;
|
|
||||||
--download: 0deg;
|
|
||||||
--inside-margin: 5px;
|
|
||||||
--inside-circle-dimension: 30px;
|
|
||||||
--border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.circle-wrap {
|
|
||||||
position: relative;
|
|
||||||
top: 10px;
|
|
||||||
display: inline-block;
|
|
||||||
width: var(--circle-wrap-dimension);
|
|
||||||
height: var(--circle-wrap-dimension);
|
|
||||||
background: #e6e2e7;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
.circle-wrap .circle .mask,
|
|
||||||
.circle-wrap .circle .fill {
|
|
||||||
width: var(--circle-wrap-dimension);
|
|
||||||
height: var(--circle-wrap-dimension);
|
|
||||||
position: absolute;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
.circle-wrap .circle .mask {
|
|
||||||
clip: rect(0, var(--circle-wrap-dimension), var(--circle-wrap-dimension), calc(var(--circle-wrap-dimension) / 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
.circle-wrap .circle .mask .fill {
|
|
||||||
clip: rect(0, calc(var(--circle-wrap-dimension) / 2), var(--circle-wrap-dimension), 0);
|
|
||||||
background-color: #3498db;
|
|
||||||
}
|
|
||||||
.circle-wrap .circle .mask.full,
|
|
||||||
.circle-wrap .circle .fill {
|
|
||||||
transform: rotate(var(--download));
|
|
||||||
}
|
|
||||||
|
|
||||||
.circle-wrap .inside-circle {
|
|
||||||
width: var(--inside-circle-dimension);
|
|
||||||
height: var(--inside-circle-dimension);
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
background: #fff;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: var(--inside-margin);
|
|
||||||
margin-left: var(--inside-margin);
|
|
||||||
position: absolute;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.circle-wrap img {
|
|
||||||
height: 30px;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cancel-button {
|
|
||||||
position: relative;
|
|
||||||
top: 10px;
|
|
||||||
height: 40px;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
@ -1,264 +0,0 @@
|
|||||||
const units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
|
||||||
function niceBytes(x){
|
|
||||||
var unitIndex = 0;
|
|
||||||
var n = parseInt(x, 10) || 0;
|
|
||||||
while(n >= 1024 && ++unitIndex)
|
|
||||||
n = n/1024;
|
|
||||||
return(n.toFixed(n >= 10 || unitIndex < 1 ? 0 : 2) + ' ' + units[unitIndex]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getIndexById(id) {
|
|
||||||
var index = 0;
|
|
||||||
for(var i = 0; i < app.books.length; i++) {
|
|
||||||
if (app.books[i]["id"] == id) {
|
|
||||||
index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setTranslations(translations) {
|
|
||||||
app.translations = createDict(TRANSLATION_KEYS, translations);
|
|
||||||
}
|
|
||||||
|
|
||||||
const BOOK_KEYS = ["id", "name", "path", "url", "size", "description", "title", "tags", "date", "faviconUrl", "faviconMimeType", "downloadId"];
|
|
||||||
function addBook(values) {
|
|
||||||
var b = createDict(BOOK_KEYS, values);
|
|
||||||
if (b.downloadId && !downloadUpdaters.hasOwnProperty(b.id)) {
|
|
||||||
downloadUpdaters[b.id] = setInterval(function() { getDownloadInfo(b.id); }, 1000);
|
|
||||||
}
|
|
||||||
app.books.push(b);
|
|
||||||
}
|
|
||||||
function onBooksChanged () {
|
|
||||||
app.books = [];
|
|
||||||
for(var i=0; i<contentManager.bookIds.length; i++) {
|
|
||||||
var id = contentManager.bookIds[i];
|
|
||||||
contentManager.getBookInfos(id, BOOK_KEYS, addBook);
|
|
||||||
}
|
|
||||||
app.displayedBooksNb = 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onOneBookChanged (id) {
|
|
||||||
var index = getIndexById(id);
|
|
||||||
contentManager.getBookInfos(id, BOOK_KEYS, function(values) {
|
|
||||||
var b = createDict(BOOK_KEYS, values);
|
|
||||||
if (b.downloadId && !downloadUpdaters.hasOwnProperty(b.id)) {
|
|
||||||
downloadUpdaters[b.id] = setInterval(function() { getDownloadInfo(b.id); }, 1000);
|
|
||||||
}
|
|
||||||
app.books.splice(index, 1, b);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function onBookRemoved (id) {
|
|
||||||
var index = getIndexById(id);
|
|
||||||
app.books.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadUpdaters = {}
|
|
||||||
const DOWNLOAD_KEYS = ["id", "status", "followedBy", "path", "totalLength", "completedLength", "downloadSpeed", "verifiedLength"];
|
|
||||||
function getDownloadInfo(id) {
|
|
||||||
contentManager.updateDownloadInfos(id, DOWNLOAD_KEYS, function(values) {
|
|
||||||
if (values.length == 0) {
|
|
||||||
clearInterval(downloadUpdaters[id]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
d = createDict(DOWNLOAD_KEYS, values);
|
|
||||||
if (d.status == "completed") {
|
|
||||||
clearInterval(downloadUpdaters[id]);
|
|
||||||
Vue.delete(app.downloads, id);
|
|
||||||
return;
|
|
||||||
} else if (d.status == "error") {
|
|
||||||
clearInterval(downloadUpdaters[id]);
|
|
||||||
Vue.delete(app.downloads, id);
|
|
||||||
alert("Error: download failed.");
|
|
||||||
contentManager.eraseBook(id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
d["completedLengthInDegree"] = Math.trunc(d["completedLength"] * 180 / d["totalLength"]).toString() + "deg";
|
|
||||||
Vue.set(app.downloads, id, d);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function displayLoadIcon(display) {
|
|
||||||
if (display) {
|
|
||||||
document.getElementById("load-icon").classList.remove("do-not-display")
|
|
||||||
document.getElementById("bookList").classList.add("do-not-display");
|
|
||||||
} else {
|
|
||||||
document.getElementById("load-icon").classList.add("do-not-display");
|
|
||||||
document.getElementById("bookList").classList.remove("do-not-display")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const TRANSLATION_KEYS = ["search-files",
|
|
||||||
"title",
|
|
||||||
"size",
|
|
||||||
"date",
|
|
||||||
"content-type",
|
|
||||||
"reset-sort",
|
|
||||||
"open",
|
|
||||||
"delete",
|
|
||||||
"download",
|
|
||||||
"resume",
|
|
||||||
"pause",
|
|
||||||
"cancel"];
|
|
||||||
|
|
||||||
function init() {
|
|
||||||
new QWebChannel(qt.webChannelTransport, function(channel) {
|
|
||||||
contentManager = channel.objects.contentManager;
|
|
||||||
app = new Vue({
|
|
||||||
el: "#app",
|
|
||||||
data: {
|
|
||||||
contentManager: contentManager,
|
|
||||||
displayedBooksNb: 20,
|
|
||||||
books: [],
|
|
||||||
downloads: {},
|
|
||||||
activeSortType:"",
|
|
||||||
sortOrderAsc:true,
|
|
||||||
translations:{}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
gt : function(key) {
|
|
||||||
return this.translations[key];
|
|
||||||
},
|
|
||||||
openBook : function(book) {
|
|
||||||
contentManager.openBook(book.id, function() {});
|
|
||||||
},
|
|
||||||
downloadBook : function(book) {
|
|
||||||
contentManager.downloadBook(book.id, function(did) {
|
|
||||||
if (did.length == 0) {
|
|
||||||
alert("Error: this download is not available.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (did == "storage_error") {
|
|
||||||
alert("not enough storage available.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
book.downloadId = did;
|
|
||||||
downloadUpdaters[book.id] = setInterval(function() { getDownloadInfo(book.id); }, 1000);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
eraseBook : function(book) {
|
|
||||||
if (confirm("Are you sure you want to delete '" + book.title + "' ?")) {
|
|
||||||
contentManager.eraseBook(book.id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
pauseResumeBook : function(book) {
|
|
||||||
if (app.downloads[book.id].status == 'active') {
|
|
||||||
contentManager.pauseBook(book.id);
|
|
||||||
} else if (app.downloads[book.id].status == 'paused') {
|
|
||||||
contentManager.resumeBook(book.id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cancelBook : function(book) {
|
|
||||||
contentManager.pauseBook(book.id);
|
|
||||||
if (confirm("Are you sure you want to abort the download of '" + book.title + "' ?")) {
|
|
||||||
contentManager.cancelBook(book.id);
|
|
||||||
clearInterval(downloadUpdaters[book.id]);
|
|
||||||
Vue.delete(app.downloads, book.id);
|
|
||||||
} else {
|
|
||||||
contentManager.resumeBook(book.id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
displayedBooks : function(books, nb) {
|
|
||||||
var a = books.slice(0, nb);
|
|
||||||
return a;
|
|
||||||
},
|
|
||||||
getBookFromMousePosition : function() {
|
|
||||||
var elements = document.elementsFromPoint(mouseX, mouseY);
|
|
||||||
var bookId = null;
|
|
||||||
for(var i = 0; i < elements.length; i++) {
|
|
||||||
if (elements[i].localName == "summary" && elements[i].classList.contains("book-summary")) {
|
|
||||||
bookId = elements[i].id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var book = null;
|
|
||||||
for(var i = 0; i < app.books.length; i++) {
|
|
||||||
if (app.books[i]["id"] == bookId) {
|
|
||||||
book = app.books[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return book;
|
|
||||||
},
|
|
||||||
sortBookBy : function(sortBy) {
|
|
||||||
if (this.activeSortType == sortBy && this.sortOrderAsc)
|
|
||||||
this.sortOrderAsc = false;
|
|
||||||
else {
|
|
||||||
this.activeSortType = sortBy;
|
|
||||||
this.sortOrderAsc = true;
|
|
||||||
}
|
|
||||||
contentManager.setSortBy(this.activeSortType, this.sortOrderAsc);
|
|
||||||
},
|
|
||||||
isActive: function (sortType) {
|
|
||||||
return (this.activeSortType == sortType)
|
|
||||||
},
|
|
||||||
isUpOrDown: function (sortType, sortOrderAsc) {
|
|
||||||
return (sortType == this.activeSortType && this.sortOrderAsc == sortOrderAsc);
|
|
||||||
},
|
|
||||||
resetSort: function () {
|
|
||||||
this.sortOrderAsc = true;
|
|
||||||
this.activeSortType = "";
|
|
||||||
contentManager.setSortBy("unsorted", this.sortOrderAsc);
|
|
||||||
},
|
|
||||||
niceBytes : niceBytes
|
|
||||||
}
|
|
||||||
});
|
|
||||||
contentManager.booksChanged.connect(onBooksChanged);
|
|
||||||
contentManager.oneBookChanged.connect(onOneBookChanged);
|
|
||||||
contentManager.bookRemoved.connect(onBookRemoved);
|
|
||||||
contentManager.pendingRequest.connect(displayLoadIcon);
|
|
||||||
contentManager.getTranslations(TRANSLATION_KEYS, setTranslations);
|
|
||||||
onBooksChanged();
|
|
||||||
displayLoadIcon(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
futurCall = null;
|
|
||||||
function setSearch(value) {
|
|
||||||
clearTimeout(futurCall);
|
|
||||||
futurCall = setTimeout(function(){contentManager.setSearch(value)}, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
function scrolled(e) {
|
|
||||||
if (e.offsetHeight + e.scrollTop >= e.scrollHeight) {
|
|
||||||
app.displayedBooksNb = Math.min(app.displayedBooksNb+20, app.books.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("click", e => {
|
|
||||||
if (menuVisible)
|
|
||||||
displayMenu(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
var mouseX, mouseY = 0;
|
|
||||||
window.addEventListener("contextmenu", e => {
|
|
||||||
e.preventDefault();
|
|
||||||
mouseX = e.pageX;
|
|
||||||
mouseY = e.pageY;
|
|
||||||
setContextMenuPosition();
|
|
||||||
var book = app.getBookFromMousePosition();
|
|
||||||
displayMenu(book);
|
|
||||||
});
|
|
||||||
|
|
||||||
var menuVisible = false;
|
|
||||||
function displayMenu(book) {
|
|
||||||
var menu = document.getElementById("menu");
|
|
||||||
menu.style.display = (book) ? "block" : "none";
|
|
||||||
menuVisible = (book) ? true : false;
|
|
||||||
if (!book)
|
|
||||||
return;
|
|
||||||
var localElements = document.getElementsByClassName("local-option");
|
|
||||||
for(var i = 0; i < localElements.length; i++)
|
|
||||||
localElements[i].style.display = (book.path) ? "block" : "none";
|
|
||||||
document.getElementsByClassName("download-option")[0].style.display = (!book.path && !app.downloads[book.id]) ? "block" : "none";
|
|
||||||
document.getElementsByClassName("pause-option")[0].style.display = (app.downloads[book.id] && app.downloads[book.id].status == 'active') ? "block" : "none";
|
|
||||||
document.getElementsByClassName("resume-option")[0].style.display = (app.downloads[book.id] && app.downloads[book.id].status == 'paused') ? "block" : "none";
|
|
||||||
document.getElementsByClassName("cancel-option")[0].style.display = (app.downloads[book.id]) ? "block" : "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
function setContextMenuPosition() {
|
|
||||||
var menu = document.getElementById("menu");
|
|
||||||
menu.style.left = `${mouseX}px`;
|
|
||||||
menu.style.top = `${mouseY}px`;
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
function createDict(keys, values) {
|
|
||||||
var d = {}
|
|
||||||
for(var i=0; i<keys.length; i++) {
|
|
||||||
d[keys[i]] = values[i];
|
|
||||||
}
|
|
||||||
return d;
|
|
||||||
}
|
|
10947
resources/js/vue.js
10947
resources/js/vue.js
File diff suppressed because it is too large
Load Diff
@ -56,6 +56,5 @@
|
|||||||
<file>icons/new-tab-icon.svg</file>
|
<file>icons/new-tab-icon.svg</file>
|
||||||
<file>icons/library-icon.svg</file>
|
<file>icons/library-icon.svg</file>
|
||||||
<file>icons/open-file.svg</file>
|
<file>icons/open-file.svg</file>
|
||||||
<file>js/tools.js</file>
|
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<script src="qrc:///js/vue.js"></script>
|
|
||||||
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
|
||||||
<script src="qrc:///js/_contentManager.js"></script>
|
|
||||||
<script src="qrc:///js/tools.js"></script>
|
|
||||||
<link rel="stylesheet" type="text/css" href="qrc:///css/_contentManager.css"/>
|
|
||||||
</head>
|
|
||||||
<body onload="init()">
|
|
||||||
<div id="app">
|
|
||||||
<div id="searchBar">
|
|
||||||
<form>
|
|
||||||
<input id="searchInput" type="text" :placeholder="gt('search-files')" oninput="setSearch(this.value)"/>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div id="bookTable">
|
|
||||||
<div class="header">
|
|
||||||
<span class="tablecell cell0"></span>
|
|
||||||
<span v-on:click="sortBookBy('title')" :class="{ sortableBold: isActive('title') }" class="tablecell cell1 sortable"><i :class="{ arrowDown: isUpOrDown('title', true), arrowUp: isUpOrDown('title', false) }"></i> {{ gt("title") }}</span>
|
|
||||||
<span v-on:click="sortBookBy('size')" :class="{ sortableBold: isActive('size') }" class="tablecell cell2 sortable"><i :class="{ arrowDown: isUpOrDown('size', true), arrowUp: isUpOrDown('size', false) }"></i> {{ gt("size") }} </span>
|
|
||||||
<span v-on:click="sortBookBy('date')" :class="{ sortableBold: isActive('date') }" class="tablecell cell3 sortable"><i :class="{ arrowDown: isUpOrDown('date', true), arrowUp: isUpOrDown('date', false) }"></i> {{ gt("date") }} </span>
|
|
||||||
<span class="tablecell cell4"> {{ gt("content-type") }} </span>
|
|
||||||
<span class="tablecell cell5 tablerow">
|
|
||||||
<button v-on:click="resetSort()"> {{ gt("reset-sort") }} </button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div id="load-icon" class="loader"></div>
|
|
||||||
<div id="bookList" onscroll=scrolled(this)>
|
|
||||||
<div id="menu" class="menu">
|
|
||||||
<ul class="menu-options">
|
|
||||||
<li v-on:click="openBook(getBookFromMousePosition())" class="menu-option local-option">{{ gt("open") }}</li>
|
|
||||||
<li v-on:click="eraseBook(getBookFromMousePosition())" class="menu-option local-option">{{ gt("delete") }}</li>
|
|
||||||
<li v-on:click="downloadBook(getBookFromMousePosition())" class="menu-option download-option">{{ gt("download") }}</li>
|
|
||||||
<li v-on:click="resumeBook(getBookFromMousePosition())" class="menu-option resume-option">{{ gt("resume") }}</li>
|
|
||||||
<li v-on:click="pauseBook(getBookFromMousePosition())" class="menu-option pause-option">{{ gt("pause") }}</li>
|
|
||||||
<li v-on:click="cancelBook(getBookFromMousePosition())" class="menu-option cancel-option">{{ gt("cancel") }}</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<details v-for="book in displayedBooks(books, displayedBooksNb)" class="book">
|
|
||||||
<summary v-bind:id="book.id" class="tablerow book-summary">
|
|
||||||
<span class="tablecell cell0">
|
|
||||||
<img v-if="book.faviconUrl" v-bind:src="'https://' + book.faviconUrl" />
|
|
||||||
<img v-else-if="book.faviconMimeType" v-bind:src="'zim://' + book.id + '.favicon.meta'" />
|
|
||||||
</span>
|
|
||||||
<span class="tablecell cell1">
|
|
||||||
{{ book.title }}
|
|
||||||
</span>
|
|
||||||
<span class="tablecell cell2">
|
|
||||||
{{ niceBytes(book.size) }}
|
|
||||||
</span>
|
|
||||||
<span class="tablecell cell3">
|
|
||||||
{{ book.date }}
|
|
||||||
</span>
|
|
||||||
<span class="tablecell cell4">
|
|
||||||
{{ book.tags }}
|
|
||||||
</span>
|
|
||||||
<span class="tablecell cell5">
|
|
||||||
<template v-if="downloads[book.id]" class="line">
|
|
||||||
<span>
|
|
||||||
{{ niceBytes(downloads[book.id].completedLength) }} / {{ niceBytes(downloads[book.id].totalLength) }}
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<button v-if="book.path" v-on:click="openBook(book)" class="line">Open</button>
|
|
||||||
<button v-if="!book.path && !downloads[book.id]" v-on:click="downloadBook(book)" class="">Download</button>
|
|
||||||
|
|
||||||
<!-- round download progress bar -->
|
|
||||||
<template v-if="downloads[book.id]" class="line">
|
|
||||||
<div v-on:click.stop.prevent="pauseResumeBook(book)" v-bind:style="{'--download': downloads[book.id].completedLengthInDegree}" class="circle-wrap">
|
|
||||||
<div class="circle">
|
|
||||||
<div class="mask full">
|
|
||||||
<div class="fill"></div>
|
|
||||||
</div>
|
|
||||||
<div class="mask half">
|
|
||||||
<div class="fill"></div>
|
|
||||||
</div>
|
|
||||||
<div class="inside-circle">
|
|
||||||
<img v-if="downloads[book.id] && downloads[book.id].status == 'active'" src="qrc:///icons/pause-button.png">
|
|
||||||
<img v-if="downloads[book.id] && downloads[book.id].status == 'paused'" src="qrc:///icons/play-button.png">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<!-- end -->
|
|
||||||
|
|
||||||
<img class="cancel-button" v-if="downloads[book.id]" v-on:click.stop.prevent="cancelBook(book)" src="qrc:///icons/cancel-button.png">
|
|
||||||
</span>
|
|
||||||
</summary>
|
|
||||||
<p class="content">
|
|
||||||
{{ book.description }}
|
|
||||||
</p>
|
|
||||||
</details>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body></html>
|
|
@ -21,8 +21,7 @@ ContentManager::ContentManager(Library* library, kiwix::Downloader* downloader,
|
|||||||
// mp_view will be passed to the tab who will take ownership,
|
// mp_view will be passed to the tab who will take ownership,
|
||||||
// so, we don't need to delete it.
|
// so, we don't need to delete it.
|
||||||
mp_view = new ContentManagerView();
|
mp_view = new ContentManagerView();
|
||||||
mp_view->registerObject("contentManager", this);
|
mp_view->show();
|
||||||
mp_view->setHtml();
|
|
||||||
setCurrentLanguage(QLocale().name().split("_").at(0));
|
setCurrentLanguage(QLocale().name().split("_").at(0));
|
||||||
connect(mp_library, &Library::booksChanged, this, [=]() {emit(this->booksChanged());});
|
connect(mp_library, &Library::booksChanged, this, [=]() {emit(this->booksChanged());});
|
||||||
connect(this, &ContentManager::filterParamsChanged, this, &ContentManager::updateLibrary);
|
connect(this, &ContentManager::filterParamsChanged, this, &ContentManager::updateLibrary);
|
||||||
|
@ -1,29 +1,9 @@
|
|||||||
#include "contentmanagerview.h"
|
#include "contentmanagerview.h"
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QWebEngineProfile>
|
|
||||||
#include "kiwixapp.h"
|
#include "kiwixapp.h"
|
||||||
|
|
||||||
ContentManagerView::ContentManagerView(QWidget *parent)
|
ContentManagerView::ContentManagerView(QWidget *parent)
|
||||||
: QWebEngineView(parent)
|
: QTreeView(parent)
|
||||||
{
|
{
|
||||||
QWebEnginePage* page = new QWebEnginePage(KiwixApp::instance()->getProfile(), this);
|
setSortingEnabled(true);
|
||||||
setPage(page);
|
|
||||||
page->setWebChannel(&m_webChannel);
|
|
||||||
setContextMenuPolicy( Qt::NoContextMenu );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ContentManagerView::registerObject(const QString& id, QObject* object)
|
|
||||||
{
|
|
||||||
m_webChannel.registerObject(id, object);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ContentManagerView::setHtml()
|
|
||||||
{
|
|
||||||
QFile contentFile(":texts/_contentManager.html");
|
|
||||||
contentFile.open(QIODevice::ReadOnly);
|
|
||||||
auto byteContent = contentFile.readAll();
|
|
||||||
contentFile.close();
|
|
||||||
QWebEngineView::setHtml(byteContent);
|
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,13 @@
|
|||||||
#define CONTENTMANAGERVIEW_H
|
#define CONTENTMANAGERVIEW_H
|
||||||
|
|
||||||
#include <QWebEngineView>
|
#include <QWebEngineView>
|
||||||
#include <QWebChannel>
|
#include <QTreeView>
|
||||||
|
|
||||||
class ContentManagerView : public QWebEngineView
|
class ContentManagerView : public QTreeView
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ContentManagerView(QWidget *parent = Q_NULLPTR);
|
ContentManagerView(QWidget *parent = Q_NULLPTR);
|
||||||
void registerObject(const QString &id, QObject *object);
|
|
||||||
void setHtml();
|
|
||||||
private:
|
|
||||||
QWebChannel m_webChannel;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CONTENTMANAGERVIEW_H
|
#endif // CONTENTMANAGERVIEW_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user