diff --git a/YACReader/YACReader.pro b/YACReader/YACReader.pro index 7e909ae3..556e0585 100644 --- a/YACReader/YACReader.pro +++ b/YACReader/YACReader.pro @@ -65,9 +65,11 @@ win32 { } macx { - QT += macextras gui-private + QT += gui-private CONFIG += objective_c LIBS += -framework Foundation -framework ApplicationServices -framework AppKit + + lessThan(QT_MAJOR_VERSION, 6): QT += macextras } QT += network widgets core multimedia svg diff --git a/YACReader/main_window_viewer.cpp b/YACReader/main_window_viewer.cpp index b2147a26..5f67d446 100644 --- a/YACReader/main_window_viewer.cpp +++ b/YACReader/main_window_viewer.cpp @@ -731,7 +731,7 @@ void MainWindowViewer::createToolBars() // attach toolbar - comicToolBar->attachToWindow(this->windowHandle()); + comicToolBar->attachToWindow(this); #endif } diff --git a/YACReaderLibrary/YACReaderLibrary.pro b/YACReaderLibrary/YACReaderLibrary.pro index 29329c79..72f314f0 100644 --- a/YACReaderLibrary/YACReaderLibrary.pro +++ b/YACReaderLibrary/YACReaderLibrary.pro @@ -66,7 +66,8 @@ CONFIG(force_angle) { macx { LIBS += -framework Foundation -framework ApplicationServices -framework AppKit CONFIG += objective_c - QT += macextras gui-private + QT += gui-private + lessThan(QT_MAJOR_VERSION, 6): QT += macextras } #CONFIG += release @@ -294,7 +295,11 @@ HEADERS += grid_comics_view.h \ SOURCES += grid_comics_view.cpp \ comics_view_transition.cpp -RESOURCES += qml.qrc +greaterThan(QT_MAJOR_VERSION, 5) { + RESOURCES += qml6.qrc +} else { + RESOURCES += qml.qrc +} win32:RESOURCES += qml_win.qrc unix:!macx:RESOURCES += qml_win.qrc macx:RESOURCES += qml_osx.qrc diff --git a/YACReaderLibrary/library_window.cpp b/YACReaderLibrary/library_window.cpp index bdb3006f..f389372b 100644 --- a/YACReaderLibrary/library_window.cpp +++ b/YACReaderLibrary/library_window.cpp @@ -922,9 +922,6 @@ void LibraryWindow::createToolBars() libraryToolBar->addSpace(10); libraryToolBar->addAction(toggleComicsViewAction); -#ifndef Q_OS_MAC - libraryToolBar->addAction(toggleFullScreenAction); -#endif libraryToolBar->addStretch(); @@ -935,7 +932,7 @@ void LibraryWindow::createToolBars() // libraryToolBar->setMovable(false); - libraryToolBar->attachToWindow(this->windowHandle()); + libraryToolBar->attachToWindow(this); #else libraryToolBar->backButton->setDefaultAction(backAction); diff --git a/YACReaderLibrary/qml/FlowView6.qml b/YACReaderLibrary/qml/FlowView6.qml new file mode 100644 index 00000000..ff8f82f6 --- /dev/null +++ b/YACReaderLibrary/qml/FlowView6.qml @@ -0,0 +1,221 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +import Qt5Compat.GraphicalEffects + +import com.yacreader.ComicModel 1.0 + +Rectangle { + id: mainFlowContainer + + property url backgroundImageURL; + + property real backgroundBlurRadius : 100; //85; + property real backgroundBlurOpacity : 0.25; //0.35; + property bool backgroundBlurVisible : true; + + property real additionalBottomSpace : 0; + + property real verticalPadding: 12 + + property real itemsSpacing: 17 + + signal currentCoverChanged(int index) + + Rectangle { + id: background + color: "#2A2A2A" + anchors.fill: backgroundImg + } + + Image { + id: backgroundImg + width: parent.width + height: parent.height + additionalBottomSpace + source: backgroundImage + fillMode: Image.PreserveAspectCrop + smooth: true + mipmap: true + asynchronous : true + cache: false //TODO clear cache only when it is needed + opacity: 0 + visible: false + } + + FastBlur { + anchors.fill: backgroundImg + source: backgroundImg + radius: backgroundBlurRadius + opacity: backgroundBlurOpacity + visible: backgroundBlurVisible + } + + anchors.margins: 0 + + MouseArea { + anchors.fill : list + onWheel: wheel => { + + if(list.moving) + return; + + var ci + if(wheel.angleDelta.y < 0) { + ci = Math.min(list.currentIndex+1, list.count - 1); + } + else if(wheel.angleDelta.y > 0) { + ci = Math.max(0,list.currentIndex-1); + } else { + return; + } + + list.currentIndex = ci; + } + } + + ListView { + id: list + objectName: "list" + anchors.fill: parent + + property int previousIndex; + + orientation: Qt.Horizontal + pixelAligned: true + + model: comicsList + + spacing: itemsSpacing + anchors.leftMargin: Math.floor(verticalPadding * 1.1) + + snapMode: ListView.SnapToItem + + highlightFollowsCurrentItem: true + highlightRangeMode: ListView.StrictlyEnforceRange + preferredHighlightEnd: 50 + + highlightMoveDuration: 250 + + onCurrentIndexChanged: currentIndex => { + if (list.currentIndex !== -1) { + mainFlowContainer.currentCoverChanged(list.currentIndex); + } + } + + delegate: Component { + + //cover + Rectangle { + width: Math.floor((list.height - (verticalPadding * 2)) * 0.65); + height: list.height - (verticalPadding * 2); + anchors.verticalCenter: parent.verticalCenter + + color:"transparent" + + scale: mouseArea.containsMouse ? 1.025 : 1 + + Behavior on scale { + NumberAnimation { duration: 90 } + } + + BusyIndicator { + scale: 0.5 + anchors.centerIn: parent + running: coverElement.status === Image.Loading + } + + DropShadow { + transparentBorder: true + anchors.fill: coverElement + horizontalOffset: 0 + verticalOffset: 0 + radius: 6 + //samples: 17 + color: "#BB000000" + source: coverElement + visible: (Qt.platform.os === "osx") ? false : true; + } + + Image { + id: coverElement + anchors.fill: parent + source: cover_path + fillMode: Image.PreserveAspectCrop + smooth: true + mipmap: true + asynchronous : true + cache: false + } + + + //mark + Image { + id: mark + width: 23 + height: 23 + source: read_column&&show_marks?"tick.png":has_been_opened&&show_marks?"reading.png":"" + anchors {right: coverElement.right; top: coverElement.top; topMargin: 9; rightMargin: 9} + asynchronous : true + } + + //border + Rectangle { + width: coverElement.width + height: coverElement.height + anchors.centerIn: coverElement + color: "transparent" + border { + color: "#30FFFFFF" + width: 1 + } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + + hoverEnabled: true + + onDoubleClicked: { + list.currentIndex = index; + currentIndexHelper.selectedItem(index); + } + + onReleased: mouse => { + list.currentIndex = index; + + if(mouse.button === Qt.RightButton) // context menu is requested + { + var coordinates = mainFlowContainer.mapFromItem(coverElement,mouseX,mouseY) + contextMenuHelper.requestedContextMenu(Qt.point(coordinates.x,coordinates.y)); + } + + mouse.accepted = true; + } + } + } + } + + focus: true + Keys.onPressed: event => { + + if (event.modifiers & Qt.ControlModifier || event.modifiers & Qt.ShiftModifier) + return; + var ci + if (event.key === Qt.Key_Right || event.key === Qt.Key_Down) { + ci = Math.min(list.currentIndex+1, list.count - 1); + } + else if (event.key === Qt.Key_Left || event.key === Qt.Key_Up) { + ci = Math.max(0,list.currentIndex-1); + } else { + return; + } + + list.currentIndex = ci; + + event.accepted = true; + } + + } +} diff --git a/YACReaderLibrary/qml/GridComicsView6.qml b/YACReaderLibrary/qml/GridComicsView6.qml new file mode 100644 index 00000000..316da945 --- /dev/null +++ b/YACReaderLibrary/qml/GridComicsView6.qml @@ -0,0 +1,895 @@ +import QtQuick 2.15 + +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.12 + +import Qt5Compat.GraphicalEffects + +import com.yacreader.ComicModel 1.0 + +import com.yacreader.ComicInfo 1.0 +import com.yacreader.ComicDB 1.0 + +import QtQuick.Controls.Basic + +SplitView { + orientation: Qt.Horizontal + handle: Rectangle { + border.width : 0 + implicitWidth: 10 + color: info_container.color + } + +Rectangle { + id: main + clip: true + + Image { + id: backgroundImg + anchors.fill: parent + source: backgroundImage + fillMode: Image.PreserveAspectCrop + smooth: true + mipmap: true + asynchronous : true + cache: false //TODO clear cache only when it is needed + opacity: 0 + visible: false + } + + FastBlur { + anchors.fill: backgroundImg + source: backgroundImg + radius: backgroundBlurRadius + opacity: backgroundBlurOpacity + visible: backgroundBlurVisible + } + + color: backgroundColor + width: parent.width - (info_container.visible ? info_container.width : 0) + SplitView.fillWidth: true + SplitView.minimumWidth: coverWidth + 100 + height: parent.height + anchors.margins: 0 + + Component { + id: appDelegate + Rectangle + { + id: cell + width: grid.cellWidth + height: grid.cellHeight + color: "#00000000" + + scale: mouseArea.containsMouse ? 1.025 : 1 + + Behavior on scale { + NumberAnimation { duration: 90 } + } + + DropShadow { + anchors.fill: realCell + transparentBorder: true + horizontalOffset: 0 + verticalOffset: 0 + radius: 10.0 + //samples: 17 + color: "#FF000000" + source: realCell + visible: (Qt.platform.os === "osx") ? false : true; + } + + Rectangle { + id: realCell + + property int position : 0 + property bool dragging: false; + Drag.active: mouseArea.drag.active + Drag.hotSpot.x: 32 + Drag.hotSpot.y: 32 + Drag.dragType: Drag.Automatic + //Drag.mimeData: { "x": 1 } + Drag.proposedAction: Qt.CopyAction + Drag.onActiveChanged: { + if(!dragging) + { + dragManager.startDrag(); + dragging = true; + }else + dragging = false; + } + + width: itemWidth + height: itemHeight + + color: ((dummyValue || !dummyValue) && comicsSelectionHelper.isSelectedIndex(index))?selectedColor:cellColor; + //border.color: ((dummyValue || !dummyValue) && comicsSelectionHelper.isSelectedIndex(index))?selectedBorderColor:borderColor; + //border.width: ?1:0; + anchors.horizontalCenter: parent.horizontalCenter + + Rectangle + { + id: mouseOverBorder + + property bool commonBorder : false + + property int lBorderwidth : 2 + property int rBorderwidth : 2 + property int tBorderwidth : 2 + property int bBorderwidth : 2 + + property int commonBorderWidth : 1 + + z : -1 + + color: "#00000000" + + anchors + { + left: parent.left + right: parent.right + top: parent.top + bottom: parent.bottom + + topMargin : commonBorder ? -commonBorderWidth : -tBorderwidth + bottomMargin : commonBorder ? -commonBorderWidth : -bBorderwidth + leftMargin : commonBorder ? -commonBorderWidth : -lBorderwidth + rightMargin : commonBorder ? -commonBorderWidth : -rBorderwidth + } + + border.color: (Qt.platform.os === "osx") ? selectedBorderColor : "#ffcc00" + border.width: 3 + + opacity: (dummyValue || !dummyValue) && comicsSelectionHelper.isSelectedIndex(index) ? 1 : 0 + + Behavior on opacity { + NumberAnimation { duration: 300 } + } + + radius : 2 + } + + + MouseArea { + id: mouseArea + drag.target: realCell + + drag.minimumX: 0 + drag.maximumX: 0 + drag.minimumY: 0 + drag.maximumY: 0 + + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + + hoverEnabled: true + + onDoubleClicked: { + comicsSelectionHelper.clear(); + + comicsSelectionHelper.selectIndex(index); + grid.currentIndex = index; + currentIndexHelper.selectedItem(index); + } + + function selectAll(from,to) + { + for(var i = from;i<=to;i++) + { + comicsSelectionHelper.selectIndex(i); + } + } + + onPressed: mouse => { + var ci = grid.currentIndex; //save current index + + /*if(mouse.button != Qt.RightButton && !(mouse.modifiers & Qt.ControlModifier || mouse.modifiers & Qt.ShiftModifier)) + { + if(!comicsSelectionHelper.isSelectedIndex(index)) + comicsSelectionHelper.clear(); + }*/ + + if(mouse.modifiers & Qt.ShiftModifier) + if(index < ci) + { + selectAll(index,ci); + grid.currentIndex = index; + } + else if (index > ci) + { + selectAll(ci,index); + grid.currentIndex = index; + } + + mouse.accepted = true; + + if(mouse.button === Qt.RightButton) // context menu is requested + { + if(!comicsSelectionHelper.isSelectedIndex(index)) //the context menu is requested outside the current selection, the selection will be + { + currentIndexHelper.setCurrentIndex(index) + grid.currentIndex = index; + } + + var coordinates = main.mapFromItem(realCell,mouseX,mouseY) + contextMenuHelper.requestedContextMenu(Qt.point(coordinates.x,coordinates.y)); + mouse.accepted = false; + + } else //left button + { + + if(mouse.modifiers & Qt.ControlModifier) + { + if(comicsSelectionHelper.isSelectedIndex(index)) + { + if(comicsSelectionHelper.numItemsSelected()>1) + { + comicsSelectionHelper.deselectIndex(index); + if(grid.currentIndex === index) + grid.currentIndex = comicsSelectionHelper.lastSelectedIndex(); + } + } + else + { + comicsSelectionHelper.selectIndex(index); + grid.currentIndex = index; + } + } + + if(mouse.button !== Qt.RightButton && !(mouse.modifiers & Qt.ControlModifier || mouse.modifiers & Qt.ShiftModifier)) //just left button click + { + if(comicsSelectionHelper.isSelectedIndex(index)) //the context menu is requested outside the current selection, the selection will be + { + + } + else + { + currentIndexHelper.setCurrentIndex(index) + } + + grid.currentIndex = index; + } + } + + } + + onReleased: mouse => { + if(mouse.button === Qt.LeftButton && !(mouse.modifiers & Qt.ControlModifier || mouse.modifiers & Qt.ShiftModifier)) + { + if(comicsSelectionHelper.isSelectedIndex(index)) + { + currentIndexHelper.setCurrentIndex(index) + grid.currentIndex = index; + } + } + } + } + } + + /**/ + + //cover + Image { + id: coverElement + width: coverWidth + height: coverHeight + anchors {horizontalCenter: parent.horizontalCenter; top: realCell.top; topMargin: 0} + source: cover_path + fillMode: Image.PreserveAspectCrop + smooth: true + mipmap: true + asynchronous : true + cache: false //TODO clear cache only when it is needed + + } + + //border + Rectangle { + width: coverElement.width + height: coverElement.height + anchors {horizontalCenter: parent.horizontalCenter; top: realCell.top; topMargin: 0} + color: "transparent" + border { + color: "#20FFFFFF" + width: 1 + } + } + + //mark + Image { + id: mark + width: 23 + height: 23 + source: read_column&&show_marks?"tick.png":has_been_opened&&show_marks?"reading.png":"" + anchors {right: coverElement.right; top: coverElement.top; topMargin: 9; rightMargin: 9} + asynchronous : true + } + + //title + Text { + id : titleText + anchors { top: coverElement.bottom; left: realCell.left; leftMargin: 4; rightMargin: 4; topMargin: 4; } + width: itemWidth - 8 + maximumLineCount: 2 + wrapMode: Text.WordWrap + text: title + elide: Text.ElideRight + color: titleColor + clip: true + font.letterSpacing: fontSpacing + font.pointSize: fontSize + font.family: fontFamily + } + + //number + Text { + anchors {bottom: realCell.bottom; left: realCell.left; margins: 4} + text: number?"#"+number:"" + color: textColor + font.letterSpacing: fontSpacing + font.pointSize: fontSize + font.family: fontFamily + } + + //page icon + Image { + id: pageImage + anchors {bottom: realCell.bottom; right: realCell.right; bottomMargin: 5; rightMargin: 4; leftMargin: 4} + source: "page.png" + width: 8 + height: 10 + } + + //numPages + Text { + id: pages + anchors {bottom: realCell.bottom; right: pageImage.left; margins: 4} + text: has_been_opened?current_page+"/"+num_pages:num_pages + color: textColor + font.letterSpacing: fontSpacing + font.pointSize: fontSize + font.family: fontFamily + } + + //rating icon + Image { + id: ratingImage + anchors {bottom: realCell.bottom; right: pageImage.left; bottomMargin: 5; rightMargin: Math.floor(pages.width)+12} + source: "star.png" + width: 13 + height: 11 + + MouseArea { + anchors.fill: parent + onPressed: { + console.log("rating"); + comicsSelectionHelper.clear(); + comicsSelectionHelper.selectIndex(index); + grid.currentIndex = index; + ratingConextMenu.popup(); + } + } + + Menu { + background: Rectangle { + implicitWidth: 42 + implicitHeight: 100 + //border.color: "#222" + //color: "#444" + } + + id: ratingConextMenu + + Action { text: "1"; enabled: true; onTriggered: comicRatingHelper.rate(index,1) } + Action { text: "2"; enabled: true; onTriggered: comicRatingHelper.rate(index,2) } + Action { text: "3"; enabled: true; onTriggered: comicRatingHelper.rate(index,3) } + Action { text: "4"; enabled: true; onTriggered: comicRatingHelper.rate(index,4) } + Action { text: "5"; enabled: true; onTriggered: comicRatingHelper.rate(index,5) } + + delegate: MenuItem { + implicitHeight: 30 + } + } + } + + //comic rating + Text { + id: comicRating + anchors {bottom: realCell.bottom; right: ratingImage.left; margins: 4} + text: rating>0?rating:"-" + color: textColor + } + } + } + + Rectangle { + id: scrollView + objectName: "topScrollView" + anchors.fill: parent + anchors.margins: 0 + children: grid + + color: "transparent" + + function scrollToOrigin() { + grid.contentY = grid.originY + grid.contentX = grid.originX + } + + DropArea { + anchors.fill: parent + + onEntered: { + if(drag.hasUrls) + { + if(dropManager.canDropUrls(drag.urls, drag.action)) + { + drag.accepted = true; + }else + drag.accepted = false; + } + else if (dropManager.canDropFormats(drag.formats)) { + drag.accepted = true; + } else + drag.accepted = false; + } + + onDropped: { + if(drop.hasUrls && dropManager.canDropUrls(drop.urls, drop.action)) + { + dropManager.droppedFiles(drop.urls, drop.action); + } + else{ + if (dropManager.canDropFormats(drop.formats)) + { + var destItem = grid.itemAt(drop.x,drop.y + grid.contentY); + var destLocalX = grid.mapToItem(destItem,drop.x,drop.y + grid.contentY).x + var realIndex = grid.indexAt(drop.x,drop.y + grid.contentY); + + if(realIndex === -1) + realIndex = grid.count - 1; + + var destIndex = destLocalX < (grid.cellWidth / 2) ? realIndex : realIndex + 1; + dropManager.droppedComicsForResortingAt(drop.getDataAsString(), destIndex); + } + } + } + } + + property Component currentComicView: Component { + id: currentComicView + Rectangle { + id: currentComicViewTopView + color: "#00000000" + + height: showCurrentComic ? 270 : 20 + + Rectangle { + color: (Qt.platform.os === "osx") ? "#88FFFFFF" : "#88000000" + + id: currentComicVisualView + + width: main.width + height: 250 + + visible: showCurrentComic + + //cover + Image { + id: currentCoverElement + anchors.fill: parent + + anchors.leftMargin: 15 + anchors.topMargin: 15 + anchors.bottomMargin: 15 + anchors.rightMargin: 15 + horizontalAlignment: Image.AlignLeft + anchors {horizontalCenter: parent.horizontalCenter; top: parent.top; topMargin: 0} + source: comicsList.getCoverUrlPathForComicHash(currentComicInfo.hash.toString()) + fillMode: Image.PreserveAspectFit + smooth: true + mipmap: true + asynchronous : true + cache: false //TODO clear cache only when it is needed + } + + DropShadow { + anchors.fill: currentCoverElement + horizontalOffset: 0 + verticalOffset: 0 + radius: 8.0 + transparentBorder: true + //samples: 17 + color: "#FF000000" + source: currentCoverElement + visible: (Qt.platform.os === "osx") ? false : true; + } + + ColumnLayout + { + id: currentComicInfoView + + x: currentCoverElement.anchors.rightMargin + currentCoverElement.paintedWidth + currentCoverElement.anchors.rightMargin + //y: currentCoverElement.anchors.topMargin + + anchors.top: currentCoverElement.top + anchors.right: parent.right + anchors.left: readButton.left + + spacing: 9 + + Text { + Layout.topMargin: 7 + Layout.fillWidth: true + Layout.rightMargin: 20 + + Layout.alignment: Qt.AlignTop | Qt.AlignLeft + + id: currentComicInfoTitleView + + color: infoTitleColor + font.family: "Arial" + font.bold: true + font.pixelSize: 21 + wrapMode: Text.WordWrap + + text: currentComic.getTitleIncludingNumber() + } + + Flow { + spacing: 0 + Layout.alignment: Qt.AlignTop | Qt.AlignLeft + Layout.fillWidth: true + Layout.fillHeight: false + + id: currentComicDetailsFlowView + property font infoFont: Qt.font({ + family: "Arial", + pixelSize: 14 + }); + property string infoFlowTextColor: infoTextColor + + Text { + id: currentComicInfoVolume + color: currentComicDetailsFlowView.infoFlowTextColor + font: currentComicDetailsFlowView.infoFont + text: currentComicInfo.volume ? currentComicInfo.volume : "" + rightPadding: 20 + visible: currentComicInfo.volume ? true : false + } + + Text { + id: currentComicInfoNumbering + color: currentComicDetailsFlowView.infoFlowTextColor + font: currentComicDetailsFlowView.infoFont + text: currentComicInfo.number + "/" + currentComicInfo.count + rightPadding: 20 + visible : currentComicInfo.number ? true : false + } + + Text { + id: currentComicInfoGenre + color: currentComicDetailsFlowView.infoFlowTextColor + font: currentComicDetailsFlowView.infoFont + text: currentComicInfo.genere ? currentComicInfo.genere : "" + rightPadding: 20 + visible: currentComicInfo.genere ? true : false + } + + Text { + id: currentComicInfoDate + color: currentComicDetailsFlowView.infoFlowTextColor + font: currentComicDetailsFlowView.infoFont + text: currentComicInfo.date ? currentComicInfo.date : "" + rightPadding: 20 + visible: currentComicInfo.date ? true : false + } + + Text { + id: currentComicInfoPages + color: currentComicDetailsFlowView.infoFlowTextColor + font: currentComicDetailsFlowView.infoFont + text: (currentComicInfo.numPages ? currentComicInfo.numPages : "") + " pages" + rightPadding: 20 + visible: currentComicInfo.numPages ? true : false + } + + Text { + id: currentComicInfoShowInComicVine + font: currentComicDetailsFlowView.infoFont + color: "#ffcc00" + text: "Show in Comic Vine" + visible: currentComicInfo.comicVineID ? true : false + MouseArea { + anchors.fill: parent + onClicked: { + Qt.openUrlExternally("http://www.comicvine.com/comic/4000-%1/".arg(comicInfo.comicVineID)); + } + } + } + } + + ScrollView { + Layout.topMargin: 6 + Layout.rightMargin: 30 + Layout.bottomMargin: 5 + Layout.fillWidth: true + Layout.maximumHeight: (currentComicVisualView.height * 0.32) + Layout.maximumWidth: 960 + + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + + ScrollBar.vertical: ScrollBar { + visible: true + contentItem: Item { + implicitWidth: 12 + implicitHeight: 26 + Rectangle { + color: "#424246" + anchors.fill: parent + anchors.topMargin: 6 + anchors.leftMargin: 5 + anchors.rightMargin: 4 + anchors.bottomMargin: 6 + radius: 2 + } + } + } + + contentItem: currentComicInfoSinopsis + + id: synopsisScroller + + clip: true + + Text { + Layout.maximumWidth: 960 + + width: synopsisScroller.width + + id: currentComicInfoSinopsis + color: infoTitleColor + font.family: "Arial" + font.pixelSize: 14 + wrapMode: Text.WordWrap + + text: '' + currentComicInfo.synopsis ?? "" + '' + visible: currentComicInfo.synopsis ?? false + textFormat: Text.RichText + } + } + } + + Button { + containmentMask: null + text: "Read" + id: readButton + x: currentCoverElement.anchors.rightMargin + currentCoverElement.paintedWidth + currentCoverElement.anchors.rightMargin + anchors.bottom: currentCoverElement.bottom + anchors.bottomMargin: 15 + + onClicked: comicOpener.triggerOpenCurrentComic() + background: Rectangle { + implicitWidth: 100 + implicitHeight: 30 + border.width: readButton.activeFocus ? 2 : 1 + border.color: "#FFCC00" + radius: height / 2 + color: "#FFCC00" + } + + contentItem: Text { + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + font.family: "Arial" + font.pointSize: 12 + font.bold: true + color: "white" + text: readButton.text + } + } + + + DropShadow { + anchors.fill: readButton + transparentBorder: true + horizontalOffset: 0 + verticalOffset: 0 + radius: 8.0 + //samples: 17 + color: "#AA000000" + source: readButton + visible: ((Qt.platform.os === "osx") ? false : true) && !readButton.pressed + } + } + } + } + + GridView { + id:grid + objectName: "grid" + anchors.fill: parent + cellHeight: cellCustomHeight + header: currentComicView + focus: true + model: comicsList + delegate: appDelegate + anchors.topMargin: 0 + anchors.bottomMargin: 10 + anchors.leftMargin: 0 + anchors.rightMargin: 0 + pixelAligned: true + highlightFollowsCurrentItem: true + + currentIndex: 0 + cacheBuffer: 0 + + interactive: true + + move: Transition { + NumberAnimation { properties: "x,y"; duration: 250 } + } + + moveDisplaced: Transition { + NumberAnimation { properties: "x,y"; duration: 250 } + } + + remove: Transition { + ParallelAnimation { + NumberAnimation { property: "opacity"; to: 0; duration: 250 } + + } + } + + removeDisplaced: Transition { + NumberAnimation { properties: "x,y"; duration: 250 } + } + + + + displaced: Transition { + NumberAnimation { properties: "x,y"; duration: 250 } + } + + function numCellsPerRow() { + return Math.floor(width / cellCustomWidth); + } + + onWidthChanged: { + calculateCellWidths(cellCustomWidth); + } + + function calculateCellWidths(cWidth) { + var wholeCells = Math.floor(width / cWidth); + var rest = width - (cWidth * wholeCells) + + grid.cellWidth = cWidth + Math.floor(rest / wholeCells); + } + + WheelHandler { + onWheel: { + if (grid.contentHeight <= grid.height) { + return; + } + + var newValue = Math.min((grid.contentHeight - grid.height - (showCurrentComic ? 270 : 20)), (Math.max(grid.originY , grid.contentY - event.angleDelta.y))); + grid.contentY = newValue; + } + } + + ScrollBar.vertical: ScrollBar { + visible: grid.contentHeight > grid.height + + contentItem: Item { + implicitWidth: 12 + implicitHeight: 26 + Rectangle { + color: "#88424242" + anchors.fill: parent + anchors.topMargin: 6 + anchors.leftMargin: 3 + anchors.rightMargin: 2 + anchors.bottomMargin: 6 + border.color: "#AA313131" + border.width: 1 + radius: 3.5 + } + } + } + + Keys.onPressed: { + if (event.modifiers & Qt.ControlModifier || event.modifiers & Qt.ShiftModifier) { + event.accepted = true + return; + } + + var numCells = grid.numCellsPerRow(); + var ci = 0; + if (event.key === Qt.Key_Right) { + ci = Math.min(grid.currentIndex+1,grid.count - 1); + } + else if (event.key === Qt.Key_Left) { + ci = Math.max(0,grid.currentIndex-1); + } + else if (event.key === Qt.Key_Up) { + ci = Math.max(0,grid.currentIndex-numCells); + } + else if (event.key === Qt.Key_Down) { + ci = Math.min(grid.currentIndex+numCells,grid.count - 1); + } else { + return; + } + + event.accepted = true; + grid.currentIndex = -1 + comicsSelectionHelper.clear(); + currentIndexHelper.setCurrentIndex(ci); + grid.currentIndex = ci; + } + } + } +} + +Rectangle { + id: info_container + objectName: "infoContainer" + SplitView.preferredWidth: 350 + SplitView.minimumWidth: 350 + SplitView.maximumWidth: 960 + height: parent.height + + color: infoBackgroundColor + + visible: showInfo + + Flickable{ + id: infoFlickable + anchors.fill: parent + anchors.margins: 0 + + contentWidth: infoView.width + contentHeight: infoView.height + + ComicInfoView { + id: infoView + width: info_container.width + } + + WheelHandler { + onWheel: { + if (infoFlickable.contentHeight <= infoFlickable.height) { + return; + } + + var newValue = Math.min((infoFlickable.contentHeight - infoFlickable.height), (Math.max(infoFlickable.originY , infoFlickable.contentY - event.angleDelta.y))); + infoFlickable.contentY = newValue; + } + } + + ScrollBar.vertical: ScrollBar { + visible: infoFlickable.contentHeight > infoFlickable.height + + contentItem: Item { + implicitWidth: 12 + implicitHeight: 26 + Rectangle { + color: "#424246" + anchors.fill: parent + anchors.topMargin: 6 + anchors.leftMargin: 5 + anchors.rightMargin: 4 + anchors.bottomMargin: 6 + radius: 2 + } + } + } + } + +} +} diff --git a/YACReaderLibrary/qml/InfoFavorites6.qml b/YACReaderLibrary/qml/InfoFavorites6.qml new file mode 100644 index 00000000..11ca790d --- /dev/null +++ b/YACReaderLibrary/qml/InfoFavorites6.qml @@ -0,0 +1,32 @@ +import QtQuick 2.15 + +import Qt5Compat.GraphicalEffects + +Item { + width: 20 + height: 20 + + property bool active + + signal activeChangedByUser(bool active) + + MouseArea { + anchors.fill: favorites_button_compact + onClicked: { + activeChangedByUser(!active); + } + } + + Image { + anchors.centerIn: parent + id: favorites_button_compact + source: "info-favorites.png" + } + + ColorOverlay { + anchors.fill: favorites_button_compact + source: favorites_button_compact + color: active ? favCheckedColor : favUncheckedColor + } +} + diff --git a/YACReaderLibrary/qml/InfoRating6.qml b/YACReaderLibrary/qml/InfoRating6.qml new file mode 100644 index 00000000..8892f0a9 --- /dev/null +++ b/YACReaderLibrary/qml/InfoRating6.qml @@ -0,0 +1,48 @@ +import QtQuick 2.15 + +import Qt5Compat.GraphicalEffects + +Row { + spacing: 0 + property int rating : 0 + property int mouseIndex : 0 + + signal ratingChangedByUser(int rating) + + Repeater { + id: rating_compact + model: 5 + Item { + width: 25 + height: 20 + + Image { + id: star + source: "info-rating.png" + } + + ColorOverlay { + anchors.fill: star + source: star + color: index < (mouseIndex > 0 ? mouseIndex : rating) ? ratingSelectedColor : ratingUnselectedColor + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + + onPositionChanged: { + mouseIndex = index + 1; + } + + onClicked: { + ratingChangedByUser(mouseIndex); + } + + onExited: { + mouseIndex = 0; + } + } + } + } +} diff --git a/YACReaderLibrary/qml/InfoTick6.qml b/YACReaderLibrary/qml/InfoTick6.qml new file mode 100644 index 00000000..d9037141 --- /dev/null +++ b/YACReaderLibrary/qml/InfoTick6.qml @@ -0,0 +1,28 @@ +import QtQuick 2.15 + +import Qt5Compat.GraphicalEffects + +Item { + + property bool read + + signal readChangedByUser(bool read) + + MouseArea { + anchors.fill: read_compact + onClicked: { + readChangedByUser(!read); + } + } + + Image { + id: read_compact + source: "info-tick.png" + } + + ColorOverlay { + anchors.fill: read_compact + source: read_compact + color: read ? readTickCheckedColor : readTickUncheckedColor + } +} diff --git a/YACReaderLibrary/qml6.qrc b/YACReaderLibrary/qml6.qrc new file mode 100644 index 00000000..2fee5923 --- /dev/null +++ b/YACReaderLibrary/qml6.qrc @@ -0,0 +1,30 @@ + + + qml/GridComicsView6.qml + qml/FlowView6.qml + qml/InfoTick6.qml + qml/InfoFavorites6.qml + qml/InfoRating6.qml + qml/InfoComicsView.qml + qml/tick.png + qml/reading.png + qml/star_menu.png + qml/star_menu@2x.png + qml/info-indicator.png + qml/info-shadow.png + qml/info-indicator-light.png + qml/info-shadow-light.png + qml/info-indicator-light@2x.png + qml/info-shadow-light@2x.png + qml/info-top-shadow.png + qml/ComicInfoView.qml + qml/info-favorites.png + qml/info-favorites@2x.png + qml/info-rating.png + qml/info-rating@2x.png + qml/info-tag.png + qml/info-tag@2x.png + qml/info-tick.png + qml/info-tick@2x.png + + diff --git a/YACReaderLibrary/trayhandler.mm b/YACReaderLibrary/trayhandler.mm index 9c3cce37..c53f0897 100644 --- a/YACReaderLibrary/trayhandler.mm +++ b/YACReaderLibrary/trayhandler.mm @@ -1,3 +1,9 @@ + +#include +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#undef __OBJC_BOOL_IS_BOOL +#endif + #import #include "trayhandler.h" @@ -8,4 +14,4 @@ void OSXShowDockIcon() void OSXHideDockIcon() { [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory]; -} \ No newline at end of file +} diff --git a/YACReaderLibrary/yacreader_main_toolbar.cpp b/YACReaderLibrary/yacreader_main_toolbar.cpp index a6732193..7ddc2997 100644 --- a/YACReaderLibrary/yacreader_main_toolbar.cpp +++ b/YACReaderLibrary/yacreader_main_toolbar.cpp @@ -68,8 +68,10 @@ YACReaderMainToolBar::YACReaderMainToolBar(QWidget *parent) mainLayout->addStretch(); mainLayout->addWidget(toggleComicsViewButton, 0, Qt::AlignVCenter); +#ifndef Q_OS_MAC addWideDivider(); mainLayout->addWidget(fullscreenButton, 0, Qt::AlignVCenter); +#endif setLayout(mainLayout); diff --git a/azure-pipelines-windows-template-qt6.yml b/azure-pipelines-windows-template-qt6.yml index 707b0b37..b057ee66 100644 --- a/azure-pipelines-windows-template-qt6.yml +++ b/azure-pipelines-windows-template-qt6.yml @@ -38,20 +38,20 @@ jobs: set PATH=C:\Qt\${{ parameters.qt_version }}\${{ parameters.qt_spec }}\bin;%PATH% nmake check TESTARGS="-maxwarnings 100000" displayName: 'Run tests' -# - script: | -# set PATH=C:\Qt\${{ parameters.qt_version }}\${{ parameters.qt_spec }}\bin;%PATH% -# cd $(Build.SourcesDirectory)\ci\win -# .\create_installer.cmd ${{ parameters.architecture }} 7z $(Build.BuildNumber) -# displayName: 'Create installer' -# - task: CopyFiles@2 -# inputs: -# sourceFolder: $(Build.SourcesDirectory)\ci\win\Output\ -# contents: '*' -# targetFolder: $(Build.ArtifactStagingDirectory) -# - task: PublishPipelineArtifact@1 -# inputs: -# path: $(Build.ArtifactStagingDirectory) -# artifactName: Windows ${{ parameters.architecture }} $(Build.BuildNumber) 7z Installer + - script: | + set PATH=C:\Qt\${{ parameters.qt_version }}\${{ parameters.qt_spec }}\bin;%PATH% + cd $(Build.SourcesDirectory)\ci\win + .\create_installer.cmd ${{ parameters.architecture }} 7z $(Build.BuildNumber) qt6 + displayName: 'Create installer' + - task: CopyFiles@2 + inputs: + sourceFolder: $(Build.SourcesDirectory)\ci\win\Output\ + contents: '*' + targetFolder: $(Build.ArtifactStagingDirectory) + - task: PublishPipelineArtifact@1 + inputs: + path: $(Build.ArtifactStagingDirectory) + artifactName: Windows ${{ parameters.architecture }} $(Build.BuildNumber) 7z Qt6 Installer diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c2ee3223..d3ba0f0f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -82,6 +82,83 @@ jobs: path: $(Build.ArtifactStagingDirectory) artifactName: src $(Build.BuildNumber) tarball +# +# Linux qt6 +# + +- job: Linux_qt6 + dependsOn: CodeFormatValidation + pool: + vmImage: 'ubuntu-22.04' + steps: + - script: | + sudo apt-get update + sudo apt-get install -y qtchooser qt6-tools-dev qt6-base-dev-tools qmake6 qmake6-bin \ + qt6-base-dev qt6-multimedia-dev qt6-tools-dev-tools libgl-dev \ + libqt6opengl6-dev libunarr-dev qt6-declarative-dev libqt6svg6-dev libqt6core5compat6-dev + displayName: 'Install dependencies' + - script: | + qtchooser -list-versions + export DEFINES_VAR=DEFINES+\=\"BUILD_NUMBER=\\\\\\\"$(Build.BuildNumber)\\\\\\\"\" + qmake6 CONFIG+="unarr no_pdf" $DEFINES_VAR + qmake6 -v + make + displayName: 'Build' + - script: | + make check TESTARGS="-maxwarnings 100000" + displayName: 'Run tests' + +# +# MacOS qt6 +# + +- job: MacOS_qt6 + dependsOn: CodeFormatValidation + variables: + - ${{ if and(eq(variables['System.TeamFoundationCollectionUri'], 'https://dev.azure.com/luisangelsm/'), or(eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['Build.SourceBranch'], 'refs/heads/develop'))) }}: + - group: macos-codesign + pool: + vmImage: 'macOS-latest' + steps: + - script: | + pip3 install aqtinstall + python3 -m aqt install 6.3.1 mac desktop -m qt5compat qtmultimedia + echo '##vso[task.prependpath]$(Build.SourcesDirectory)/6.3.1/macos/bin' + brew install create-dmg + brew install node + brew link --overwrite node + npm install -g appdmg + mkdir $(Build.SourcesDirectory)\compressed_archive + wget "https://github.com/YACReader/yacreader-7z-deps/blob/main/p7zip_16.02_src_all.tar.bz2?raw=true" -O $(Build.SourcesDirectory)/compressed_archive/p7zip_16.02_src_all.tar.bz2 + tar xjf $(Build.SourcesDirectory)/compressed_archive/p7zip_16.02_src_all.tar.bz2 -C $(Build.SourcesDirectory)/compressed_archive + mv $(Build.SourcesDirectory)/compressed_archive/p7zip_16.02 $(Build.SourcesDirectory)/compressed_archive/libp7zip + displayName: 'Install dependencies' + - ${{ if and(eq(variables['System.TeamFoundationCollectionUri'], 'https://dev.azure.com/luisangelsm/'), or(eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['Build.SourceBranch'], 'refs/heads/develop'))) }}: + - task: InstallAppleCertificate@2 + inputs: + certSecureFile: 'developerID_application.p12' + certPwd: $(P12Password) + - script: | + cd $(Build.SourcesDirectory) + VERSION="$(cat common/yacreader_global.h | grep '#define VERSION "' | tr -d '#define VERSION' | tr -d '"' )" + echo "Trying to know if we are in a forked repo: $IS_FORK" + SKIP_CODESIGN="$(tr [A-Z] [a-z] <<< "$IS_FORK")" + ./compileOSX.sh $VERSION $(Build.BuildNumber) $SKIP_CODESIGN Qt6 + displayName: 'Build' + - script: | + cd $(Build.SourcesDirectory)/tests + qmake + make check TESTARGS="-maxwarnings 100000" + displayName: 'Build and run tests' + - task: CopyFiles@2 + inputs: + contents: '*.dmg' + targetFolder: $(Build.ArtifactStagingDirectory) + - task: PublishPipelineArtifact@1 + inputs: + path: $(Build.ArtifactStagingDirectory) + artifactName: Macos qt6 $(Build.BuildNumber) dmg + # # MacOS # @@ -116,7 +193,7 @@ jobs: VERSION="$(cat common/yacreader_global.h | grep '#define VERSION "' | tr -d '#define VERSION' | tr -d '"' )" echo "Trying to know if we are in a forked repo: $IS_FORK" SKIP_CODESIGN="$(tr [A-Z] [a-z] <<< "$IS_FORK")" - ./compileOSX.sh $VERSION $(Build.BuildNumber) $SKIP_CODESIGN + ./compileOSX.sh $VERSION $(Build.BuildNumber) $SKIP_CODESIGN Qt5 displayName: 'Build' - script: | cd $(Build.SourcesDirectory)/tests @@ -155,7 +232,7 @@ jobs: parameters: name: Windows_x64_qt6 architecture: 'x64' - qt_version: '6.2.2' + qt_version: '6.3.1' qt_spec: 'msvc2019_64' qt_aqt_spec: 'win64_msvc2019_64' vc_redist_url: 'https://aka.ms/vs/16/release/vc_redist.x64.exe' @@ -186,6 +263,7 @@ jobs: dependsOn: - Linux - MacOS + - MacOS_qt6 - Windows_x86 - Windows_x64 - Windows_x64_qt6 diff --git a/ci/win/build_installer_qt6.iss b/ci/win/build_installer_qt6.iss new file mode 100644 index 00000000..ad5b01f1 --- /dev/null +++ b/ci/win/build_installer_qt6.iss @@ -0,0 +1,182 @@ +[Setup] +DefaultGroupName=YACReader +LanguageDetectionMethod=locale +AppName=YACReader +AppVerName=YACReader v{#VERSION}.{#BUILD_NUMBER} +AppVersion={#VERSION}.{#BUILD_NUMBER} +VersionInfoVersion={#VERSION} +DefaultDirName={pf}\YACReader +OutputBaseFilename=YACReader-v{#VERSION}.{#BUILD_NUMBER}-win{#PLATFORM}-{#COMPRESSED_ARCHIVE_BACKEND}-qt6 +LicenseFile=COPYING.txt +AlwaysUsePersonalGroup=true +OutputDir=..\Output +ChangesAssociations=true +SetupIconFile=setup.ico +UninstallDisplayIcon=uninstall.ico +ArchitecturesInstallIn64BitMode=x64 +ArchitecturesAllowed=x64 + +[Registry] +Root: HKCR; SubKey: .cbz; ValueType: string; ValueData: Comic Book (zip); Flags: uninsdeletekey; Tasks: File_association +Root: HKCR; SubKey: Comic Book (zip); ValueType: string; ValueData: Comic Book file; Flags: uninsdeletekey; Tasks: File_association +Root: HKCR; SubKey: Comic Book (zip)\Shell\Open\Command; ValueType: string; ValueData: """{app}\YACReader.exe"" ""%1"""; Flags: uninsdeletevalue; Tasks: File_association +Root: HKCR; Subkey: Comic Book (zip)\DefaultIcon; ValueType: string; ValueData: {app}\YACReader.exe,0; Flags: uninsdeletevalue; Tasks: File_association +Root: HKCR; SubKey: .cbr; ValueType: string; ValueData: Comic Book (rar); Flags: uninsdeletekey; Tasks: File_association +Root: HKCR; SubKey: Comic Book (rar); ValueType: string; ValueData: Comic Book file; Flags: uninsdeletekey; Tasks: File_association +Root: HKCR; SubKey: Comic Book (rar)\Shell\Open\Command; ValueType: string; ValueData: """{app}\YACReader.exe"" ""%1"""; Flags: uninsdeletevalue; Tasks: File_association +Root: HKCR; Subkey: Comic Book (rar)\DefaultIcon; ValueType: string; ValueData: {app}\YACReader.exe,0; Flags: uninsdeletevalue; Tasks: File_association +Root: HKCR; Subkey: .clc; ValueType: string; ValueData: Compressed Library Covers (clc); Flags: uninsdeletekey +Root: HKCR; SubKey: Compressed Library Covers (clc); ValueType: string; ValueData: Compressed Library Covers; Flags: uninsdeletekey +Root: HKCR; Subkey: Compressed Library Covers (clc)\DefaultIcon; ValueType: string; ValueData: {app}\YACReaderLibrary.exe,1; Flags: uninsdeletevalue +Root: HKCR; Subkey: .ydb; ValueType: string; ValueData: Compressed Library Covers (clc); Flags: uninsdeletekey +Root: HKCR; SubKey: YACReader Data Base (ydb); ValueType: string; ValueData: Compressed Library Covers; Flags: uninsdeletekey +Root: HKCR; Subkey: YACReader Data Base (ydb)\DefaultIcon; ValueType: string; ValueData: {app}\YACReaderLibrary.exe,1; Flags: uninsdeletevalue + +[Files] +;Qt Frameworks +Source: Qt6Core.dll; DestDir: {app} +Source: Qt6Core5Compat.dll; DestDir: {app} +Source: Qt6Gui.dll; DestDir: {app} +Source: Qt6Multimedia.dll; DestDir: {app} +Source: Qt6Network.dll; DestDir: {app} +Source: Qt6OpenGL.dll; DestDir: {app} +Source: Qt6OpenGLWidgets.dll; DestDir: {app} +Source: Qt6Qml.dll; DestDir: {app} +Source: Qt6QmlLocalStorage.dll; DestDir: {app} +Source: Qt6QmlModels.dll; DestDir: {app} +Source: Qt6QmlWorkerScript.dll; DestDir: {app} +Source: Qt6QmlXmlListModel.dll; DestDir: {app} +Source: Qt6Quick.dll; DestDir: {app} +Source: Qt6QuickControls2.dll; DestDir: {app} +Source: Qt6QuickControls2Impl.dll; DestDir: {app} +Source: Qt6QuickDialogs2.dll; DestDir: {app} +Source: Qt6QuickDialogs2QuickImpl.dll; DestDir: {app} +Source: Qt6QuickDialogs2Utils.dll; DestDir: {app} +Source: Qt6QuickLayouts.dll; DestDir: {app} +Source: Qt6QuickParticles.dll; DestDir: {app} +Source: Qt6QuickShapes.dll; DestDir: {app} +Source: Qt6QuickTemplates2.dll; DestDir: {app} +Source: Qt6QuickWidgets.dll; DestDir: {app} +Source: Qt6Sql.dll; DestDir: {app} +Source: Qt6Svg.dll; DestDir: {app} +Source: Qt6Widgets.dll; DestDir: {app} + +;Qt Angle +Source: D3Dcompiler_47.dll; DestDir: {app} +Source: opengl32sw.dll; DestDir: {app} + +;Qt QML +Source: QtQml\*; DestDir: {app}\QtQml\; Flags: recursesubdirs +Source: QtQuick\*; DestDir: {app}\QtQuick\; Flags: recursesubdirs + +;Qt5 Compat +Source: Qt5Compat\*; DestDir: {app}\Qt5Compat\; Flags: recursesubdirs + +;Qt PlugIns +Source:iconengines\*; DestDir: {app}\iconengines\ +Source:imageformats\*; DestDir: {app}\imageformats\ +Source:networkinformation\*; DestDir: {app}\networkinformation\ +Source:platforms\*; DestDir: {app}\platforms\ +Source:qmltooling\*; DestDir: {app}\qmltooling\ +Source:sqldrivers\qsqlite.dll; DestDir: {app}\sqldrivers\ +Source:styles\*; DestDir: {app}\styles\ +Source:tls\*; DestDir: {app}\tls\ +Source:translations\*; DestDir: {app}\translations\ + + +;Libs +Source: pdfium.dll; DestDir: {app} +Source: qrencode.dll; DestDir: {app} +Source: openssl\*; DestDir: {app} + +;vcredist +Source: "vc_redist.{#PLATFORM}.exe"; DestDir: {tmp}; Flags: deleteafterinstall + +;Utils +;Source: utils\7zip.exe; DestDir: {app}\utils\ +Source: utils\7z.dll; DestDir: {app}\utils\ + +;Bin +Source: YACReader.exe; DestDir: {app}; Permissions: everyone-full +Source: YACReaderLibrary.exe; DestDir: {app}; Permissions: everyone-full; Tasks: +Source: YACReaderLibraryServer.exe; DestDir: {app}; Permissions: everyone-full; Tasks: + +;License +Source: README.md; DestDir: {app}; Flags: isreadme +Source: COPYING.txt; DestDir: {app} + +;Languages +Source: languages\*; DestDir: {app}\languages\; Flags: recursesubdirs +;Server +Source: server\*; DestDir: {app}\server\; Flags: recursesubdirs + +[Dirs] +Name: {app}; Permissions: everyone-full + +[CustomMessages] +App=YACReader +AppLibrary=YACReaderLibrary +LaunchYACReaderLibrary=Start YACreaderLibrary after finishing installation +LaunchYACReader=Start YACreader after finishing installation + +[Run] +Filename: {tmp}\vc_redist.{#PLATFORM}.exe; \ +Parameters: "/uninstall /quiet /norestart"; \ +StatusMsg: "Uninstalling VC++ Redistributables..." + +Filename: {tmp}\vc_redist.{#PLATFORM}.exe; \ +Parameters: "/install /quiet /norestart"; \ +StatusMsg: "Installing VC++ Redistributables..." + +Filename: {app}\{cm:AppLibrary}.exe; Description: {cm:LaunchYACReaderLibrary,{cm:AppLibrary}}; Flags: nowait postinstall skipifsilent +Filename: {app}\{cm:App}.exe; Description: {cm:LaunchYACReader,{cm:App}}; Flags: nowait postinstall skipifsilent + +[Icons] +Name: {group}\YACReader; Filename: {app}\YACReader.exe; WorkingDir: {app}; IconIndex: 0 +Name: {group}\YACReader Library; Filename: {app}\YACReaderLibrary.exe; WorkingDir: {app}; IconIndex: 0 +;Name: {group}\YACReader Library Package; Filename: {app}\YACReaderLibrary.exe; WorkingDir: {app}; IconIndex: 0 +[Tasks] +Name: File_association; Description: Associate .cbz and .cbr files with YACReader +;Name: StartYACReaderAfterInstall; Description: Run YACReader after install +;Name: StartYACReaderLibraryAfterInstall; Description: Run YACReaderLibrary after install + +[ThirdPartySettings] +CompileLogMethod=append + +[Code] +var donationPage: TOutputMsgWizardPage; +var URLLabel: TNewStaticText; + +procedure URLLabelOnClick(Sender: TObject); +var ErrorCode: Integer; +begin +ShellExec('open', 'https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=5TAMNQCDDMVP8&item_name=YACReader¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted', '', '', SW_SHOWNORMAL, ewNoWait, ErrorCode); +end; + +procedure InitializeWizard(); + +begin + + URLLabel := TNewStaticText.Create(WizardForm); + URLLabel.Caption:='Make a DONATION/Haz una DONACIÓN'; + URLLabel.Cursor:=crHand; + URLLabel.OnClick:=@URLLabelOnClick; + URLLabel.Parent:=WizardForm; + // Alter Font + URLLabel.Font.Style:=URLLabel.Font.Style + [fsUnderline]; + URLLabel.Font.Color:=clBlue; + URLLabel.Top:=250; + + URLLabel.Left:=35; + +donationPage := CreateOutputMsgPage(wpWelcome, + 'Iformation', 'Please read the following information before continuing.', + 'YACReader is FREE software. If you like it, please, consider to make a DONATION'#13#13 + + 'YACReader es software libre y GRATUITO. Si te gusta, por favor, considera realizar una DONACI�N'#13#13) + +end; + +procedure CurPageChanged(CurPageID: Integer); +begin +if CurPageID=donationPage.ID then URLLabel.Visible:=true else URLLabel.Visible:=false; +end; diff --git a/ci/win/create_installer.cmd b/ci/win/create_installer.cmd index bb0cf09a..c58d066b 100644 --- a/ci/win/create_installer.cmd +++ b/ci/win/create_installer.cmd @@ -58,6 +58,10 @@ if "%1"=="x86" ( type copy_build_installer.iss > build_installer.iss ) -iscc /DVERSION=%VERSION% /DPLATFORM=%1 /DCOMPRESSED_ARCHIVE_BACKEND=%2 /DBUILD_NUMBER=%3 build_installer.iss || exit /b +if "%4"=="qt6" ( + iscc /DVERSION=%VERSION% /DPLATFORM=%1 /DCOMPRESSED_ARCHIVE_BACKEND=%2 /DBUILD_NUMBER=%3 build_installer_qt6.iss || exit /b +) else ( + iscc /DVERSION=%VERSION% /DPLATFORM=%1 /DCOMPRESSED_ARCHIVE_BACKEND=%2 /DBUILD_NUMBER=%3 build_installer.iss || exit /b +) cd .. \ No newline at end of file diff --git a/common/pdf_comic.mm b/common/pdf_comic.mm index 5cef6e7c..18d3571b 100644 --- a/common/pdf_comic.mm +++ b/common/pdf_comic.mm @@ -1,5 +1,9 @@ #include "pdf_comic.h" +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#undef __OBJC_BOOL_IS_BOOL +#endif + #import #import #import diff --git a/compileOSX.sh b/compileOSX.sh index f9cc029e..6dd091d2 100755 --- a/compileOSX.sh +++ b/compileOSX.sh @@ -1,13 +1,17 @@ #! /bin/bash set -e -VERSION=${1:-"9.5.0"} +VERSION=${1:-"9.9.1"} BUILD_NUMBER=${2:-"0"} SKIP_CODESIGN=${3:-false} -if [ "$4" == "clean" ]; then +QT_VERSION=${4:-""} + +echo "building macos binaries with these params: ${VERSION} ${BUILD_NUMBER} ${SKIP_CODESIGN} ${QT_VERSION}" + +if [ "$5" == "clean" ]; then ./cleanOSX.sh fi @@ -73,8 +77,8 @@ fi echo "Preparing apps for release, Done." -echo "Copying to destination folder" -dest="YACReader-$VERSION.$BUILD_NUMBER MacOSX-Intel" +dest="YACReader-$VERSION.$BUILD_NUMBER MacOSX-Intel ${QT_VERSION}" +echo "Copying to destination folder ${dest}" mkdir -p "$dest" cp -R YACReader.app "${dest}/YACReader.app" cp -R YACReaderLibrary.app "${dest}/YACReaderLibrary.app" @@ -83,18 +87,11 @@ cp -R YACReaderLibraryServer.app "${dest}/YACReaderLibraryServer" cp COPYING.txt "${dest}/" cp README.md "${dest}/" -#mkdir -p "${dest}/icons/" -#cp images/db.png "${dest}/icons/" -#cp images/coversPackage.png "${dest}/icons/" - echo "Creating dmg package" -#tar -czf "${dest}".tar.gz "${dest}" -#hdiutil create "${dest}".dmg -srcfolder "./${dest}" -ov - -#create-dmg --volname "YACReader $VERSION.$BUILD_NUMBER Installer" --volicon icon.icns --window-size 600 403 --icon-size 128 --app-drop-link 485 90 --background background.png --icon YACReader 80 90 --icon YACReaderLibrary 235 90 --eula COPYING.txt --icon YACReaderLibraryServer 470 295 --icon README.md 120 295 --icon COPYING.txt 290 295 "$dest.dmg" "$dest" sed -i'' -e "s/#VERSION#/$VERSION/g" dmg.json sed -i'' -e "s/#BUILD_NUMBER#/$BUILD_NUMBER/g" dmg.json +sed -i'' -e "s/#QT_VERSION#/$QT_VERSION/g" dmg.json appdmg dmg.json "$dest.dmg" if [ "$SKIP_CODESIGN" = false ]; then diff --git a/config.pri b/config.pri index 711b3781..f5438863 100644 --- a/config.pri +++ b/config.pri @@ -3,7 +3,7 @@ # for a more detailed description, see INSTALL.TXT CONFIG += c++17 -win32:QMAKE_CXXFLAGS += /std:c++17 #enable c++17 explicitly in msvc +win32:QMAKE_CXXFLAGS += /std:c++17 /Zc:__cplusplus /permissive- #enable c++17 explicitly in msvc DEFINES += NOMINMAX @@ -46,8 +46,10 @@ isEmpty(QMAKE_TARGET.arch) { QMAKE_TARGET.arch = $$QMAKE_HOST.arch } contains(QMAKE_TARGET.arch, arm.*)|contains(QMAKE_TARGET.arch, aarch.*) { - message("Building for ARM architecture. Disabling OpenGL coverflow ...") - CONFIG += no_opengl + !macx { # Apple silicon supports opengl + CONFIG += no_opengl + message("Building for ARM architecture. Disabling OpenGL coverflow. If you know that your ARM arquitecture supports opengl, please edit config.pri to enable it.") + } } # build without opengl widget support diff --git a/custom_widgets/help_about_dialog.cpp b/custom_widgets/help_about_dialog.cpp index 19cd8f00..818adc80 100644 --- a/custom_widgets/help_about_dialog.cpp +++ b/custom_widgets/help_about_dialog.cpp @@ -1,5 +1,7 @@ #include "help_about_dialog.h" +#include "opengl_checker.h" + #include #include #include @@ -98,6 +100,7 @@ void HelpAboutDialog::loadSystemInfo() QString text; text.append("SYSTEM INFORMATION\n"); + text.append(QString("Qt version: %1\n").arg(qVersion())); text.append(QString("Build ABI: %1\n").arg(QSysInfo::buildAbi())); text.append(QString("build CPU architecture: %1\n").arg(QSysInfo::buildCpuArchitecture())); text.append(QString("CPU architecture: %1\n").arg(QSysInfo::currentCpuArchitecture())); @@ -124,8 +127,10 @@ void HelpAboutDialog::loadSystemInfo() // text.append(QString("CPU: %1\n").arg(tempOutput)); // } + auto openGLChecker = OpenGLChecker(); text.append("\nGRAPHIC INFORMATION\n"); text.append(QString("Screen pixel ratio: %1\n").arg(devicePixelRatioF())); + text.append(QString("OpenGL version: %1\n").arg(openGLChecker.textVersionDescription())); // if (QSysInfo::kernelType() == "winnt") { // QString gpu = "wmic PATH Win32_videocontroller get VideoProcessor"; diff --git a/custom_widgets/yacreader_macosx_toolbar.h b/custom_widgets/yacreader_macosx_toolbar.h index 35b69f18..7ee40cca 100644 --- a/custom_widgets/yacreader_macosx_toolbar.h +++ b/custom_widgets/yacreader_macosx_toolbar.h @@ -1,8 +1,11 @@ #ifndef YACREADER_MACOSX_TOOLBAR_H #define YACREADER_MACOSX_TOOLBAR_H +#include +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include #include +#include #include "yacreader_global.h" @@ -70,6 +73,8 @@ public: // convenience method for switching the icon of the view selector void updateViewSelectorIcon(const QIcon &icon); + void attachToWindow(QMainWindow *window); + signals: public slots: @@ -80,5 +85,48 @@ protected: bool yosemite; QMacToolBarItem *viewSelector; }; +#else + +#ifdef YACREADER_LIBRARY + +#include "yacreader_main_toolbar.h" +#include "yacreader_search_line_edit.h" +#include + +class YACReaderMacOSXSearchLineEdit : public YACReaderSearchLineEdit +{ +}; + +class YACReaderMacOSXToolbar : public YACReaderMainToolBar +{ +public: + explicit YACReaderMacOSXToolbar(QWidget *parent = 0); + QSize sizeHint() const override; + void addAction(QAction *action); + void addSpace(int size); // size in points + void addStretch(); + YACReaderMacOSXSearchLineEdit *addSearchEdit(); + void updateViewSelectorIcon(const QIcon &icon); + void attachToWindow(QMainWindow *window); + +private: + void paintEvent(QPaintEvent *) override; +}; + +#else + +#include + +class YACReaderMacOSXToolbar : public QToolBar +{ +public: + explicit YACReaderMacOSXToolbar(QWidget *parent = 0); + void attachToWindow(QMainWindow *window); + void addStretch(); +}; + +#endif + +#endif #endif // YACREADER_MACOSX_TOOLBAR_H diff --git a/custom_widgets/yacreader_macosx_toolbar.mm b/custom_widgets/yacreader_macosx_toolbar.mm index e5008868..7f3d6137 100644 --- a/custom_widgets/yacreader_macosx_toolbar.mm +++ b/custom_widgets/yacreader_macosx_toolbar.mm @@ -1,5 +1,10 @@ #include "yacreader_macosx_toolbar.h" +#include "QtWidgets/qmainwindow.h" +#include +#include + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include #include #include @@ -298,6 +303,11 @@ void YACReaderMacOSXToolbar::updateViewSelectorIcon(const QIcon &icon) viewSelector->setIcon(icon); } +void YACReaderMacOSXToolbar::attachToWindow(QMainWindow *window) +{ + QMacToolBar::attachToWindow(window->windowHandle()); +} + YACReaderMacOSXSearchLineEdit::YACReaderMacOSXSearchLineEdit() : QObject() { @@ -386,3 +396,108 @@ void MacToolBarItemWrapper::updateIcon(bool enabled) } else toolbaritem->setIcon(action->icon()); } +#else + +#ifdef YACREADER_LIBRARY + +YACReaderMacOSXToolbar::YACReaderMacOSXToolbar(QWidget *parent) + : YACReaderMainToolBar(parent) +{ + backButton->setIconSize(QSize(24, 24)); + forwardButton->setIconSize(QSize(24, 24)); + settingsButton->setIconSize(QSize(24, 24)); + serverButton->setIconSize(QSize(24, 24)); + helpButton->setIconSize(QSize(24, 24)); + toggleComicsViewButton->setIconSize(QSize(24, 24)); +} + +QSize YACReaderMacOSXToolbar::sizeHint() const +{ + return QSize(400, 36); +} + +void YACReaderMacOSXToolbar::addAction(QAction *action) +{ + if (backButton->defaultAction() == nullptr) { + backButton->setDefaultAction(action); + return; + } + if (forwardButton->defaultAction() == nullptr) { + forwardButton->setDefaultAction(action); + return; + } + if (settingsButton->defaultAction() == nullptr) { + settingsButton->setDefaultAction(action); + return; + } + if (serverButton->defaultAction() == nullptr) { + serverButton->setDefaultAction(action); + return; + } + if (helpButton->defaultAction() == nullptr) { + helpButton->setDefaultAction(action); + return; + } + if (toggleComicsViewButton->defaultAction() == nullptr) { + toggleComicsViewButton->setDefaultAction(action); + return; + } +} + +void YACReaderMacOSXToolbar::addSpace(int size) +{ +} + +void YACReaderMacOSXToolbar::addStretch() +{ +} + +YACReaderMacOSXSearchLineEdit *YACReaderMacOSXToolbar::addSearchEdit() +{ + auto search = new YACReaderMacOSXSearchLineEdit(); + + setSearchWidget(search); + + return search; +} + +void YACReaderMacOSXToolbar::updateViewSelectorIcon(const QIcon &icon) +{ +} + +void YACReaderMacOSXToolbar::attachToWindow(QMainWindow *window) +{ + auto toolbar = new QToolBar(); + + toolbar->addWidget(this); + toolbar->setMovable(false); + + window->addToolBar(toolbar); +} + +void YACReaderMacOSXToolbar::paintEvent(QPaintEvent *) +{ +} + +#else + +YACReaderMacOSXToolbar::YACReaderMacOSXToolbar(QWidget *parent) + : QToolBar(parent) +{ + setMovable(false); + setIconSize(QSize(24, 24)); +} + +void YACReaderMacOSXToolbar::attachToWindow(QMainWindow *window) +{ + window->setUnifiedTitleAndToolBarOnMac(true); + window->addToolBar(this); +} + +void YACReaderMacOSXToolbar::addStretch() +{ +} + +#endif + +#endif diff --git a/dmg.json b/dmg.json index 6a56f45d..26540e47 100644 --- a/dmg.json +++ b/dmg.json @@ -1,50 +1,49 @@ { - "title": "YACReader-#VERSION#.#BUILD_NUMBER#", - "icon": "icon.icns", - "background": "background.png", - "window": { - "size": { - "width": 600, - "height": 403 - } - }, - "icon-size": 128, - "contents": [ - { - "x": 485, - "y": 90, - "type": "link", - "path": "/Applications" + "title": "YACReader-#VERSION#.#BUILD_NUMBER##QT_VERSION#", + "icon": "icon.icns", + "background": "background.png", + "window": { + "size": { + "width": 600, + "height": 403 + } }, - { - "x": 80, - "y": 90, - "type": "file", - "path": "YACReader-#VERSION#.#BUILD_NUMBER# MacOSX-Intel/YACReader.app" - }, - { - "x": 235, - "y": 90, - "type": "file", - "path": "YACReader-#VERSION#.#BUILD_NUMBER# MacOSX-Intel/YACReaderLibrary.app" - }, - { - "x": 470, - "y": 295, - "type": "file", - "path": "YACReader-#VERSION#.#BUILD_NUMBER# MacOSX-Intel/YACReaderLibraryServer" - }, - { - "x": 120, - "y": 295, - "type": "file", - "path": "YACReader-#VERSION#.#BUILD_NUMBER# MacOSX-Intel/README.md" - }, - { - "x": 290, - "y": 295, - "type": "file", - "path": "YACReader-#VERSION#.#BUILD_NUMBER# MacOSX-Intel/COPYING.txt" - } - ] + "icon-size": 128, + "contents": [{ + "x": 485, + "y": 90, + "type": "link", + "path": "/Applications" + }, + { + "x": 80, + "y": 90, + "type": "file", + "path": "YACReader-#VERSION#.#BUILD_NUMBER# MacOSX-Intel #QT_VERSION#/YACReader.app" + }, + { + "x": 235, + "y": 90, + "type": "file", + "path": "YACReader-#VERSION#.#BUILD_NUMBER# MacOSX-Intel #QT_VERSION#/YACReaderLibrary.app" + }, + { + "x": 470, + "y": 295, + "type": "file", + "path": "YACReader-#VERSION#.#BUILD_NUMBER# MacOSX-Intel #QT_VERSION#/YACReaderLibraryServer" + }, + { + "x": 120, + "y": 295, + "type": "file", + "path": "YACReader-#VERSION#.#BUILD_NUMBER# MacOSX-Intel #QT_VERSION#/README.md" + }, + { + "x": 290, + "y": 295, + "type": "file", + "path": "YACReader-#VERSION#.#BUILD_NUMBER# MacOSX-Intel #QT_VERSION#/COPYING.txt" + } + ] } \ No newline at end of file