mirror of
https://github.com/kiwix/kiwix-desktop.git
synced 2025-08-03 20:56:49 -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>
|
||||
<qresource prefix="/">
|
||||
<file>js/vue.js</file>
|
||||
<file>texts/_contentManager.html</file>
|
||||
<file>css/_contentManager.css</file>
|
||||
<file>js/_contentManager.js</file>
|
||||
</qresource>
|
||||
</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/library-icon.svg</file>
|
||||
<file>icons/open-file.svg</file>
|
||||
<file>js/tools.js</file>
|
||||
</qresource>
|
||||
</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,
|
||||
// so, we don't need to delete it.
|
||||
mp_view = new ContentManagerView();
|
||||
mp_view->registerObject("contentManager", this);
|
||||
mp_view->setHtml();
|
||||
mp_view->show();
|
||||
setCurrentLanguage(QLocale().name().split("_").at(0));
|
||||
connect(mp_library, &Library::booksChanged, this, [=]() {emit(this->booksChanged());});
|
||||
connect(this, &ContentManager::filterParamsChanged, this, &ContentManager::updateLibrary);
|
||||
|
@ -1,29 +1,9 @@
|
||||
#include "contentmanagerview.h"
|
||||
#include <QFile>
|
||||
#include <QWebEngineProfile>
|
||||
#include "kiwixapp.h"
|
||||
|
||||
ContentManagerView::ContentManagerView(QWidget *parent)
|
||||
: QWebEngineView(parent)
|
||||
: QTreeView(parent)
|
||||
{
|
||||
QWebEnginePage* page = new QWebEnginePage(KiwixApp::instance()->getProfile(), this);
|
||||
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);
|
||||
setSortingEnabled(true);
|
||||
}
|
||||
|
@ -2,17 +2,13 @@
|
||||
#define CONTENTMANAGERVIEW_H
|
||||
|
||||
#include <QWebEngineView>
|
||||
#include <QWebChannel>
|
||||
#include <QTreeView>
|
||||
|
||||
class ContentManagerView : public QWebEngineView
|
||||
class ContentManagerView : public QTreeView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ContentManagerView(QWidget *parent = Q_NULLPTR);
|
||||
void registerObject(const QString &id, QObject *object);
|
||||
void setHtml();
|
||||
private:
|
||||
QWebChannel m_webChannel;
|
||||
};
|
||||
|
||||
#endif // CONTENTMANAGERVIEW_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user