Skip to content

Instantly share code, notes, and snippets.

@DavidLazarescu
Last active June 27, 2023 06:29
cmake_minimum_required(VERSION 3.16)
project(ZoomingTest VERSION 0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 6.4 REQUIRED COMPONENTS Quick)
qt_standard_project_setup()
qt_add_executable(appZoomingTest
main.cpp
)
qt_add_qml_module(appZoomingTest
URI ZoomingTest
VERSION 1.0
QML_FILES Main.qml DocumentNavigation.js
)
set_target_properties(appZoomingTest PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
target_link_libraries(appZoomingTest
PRIVATE Qt6::Quick
)
install(TARGETS appZoomingTest
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
/**
Handle the wheel usage, scroll or zoom depending on pressed modifiers
*/
function handleWheel(wheel)
{
// Normalize to factors between 0.8 and 1.2
let factor = (((wheel.angleDelta.y / 120)+1) / 6 ) + 0.8;
if (wheel.modifiers & Qt.ControlModifier)
{
zoom(factor);
}
// angleDelta.x is the "horizontal scroll" mode some mouses support by
// e.g. pushing the scroll button to the left/right. Make sure not to
// scroll vertically when a "horizontal scroll" is performed.
else if(wheel.angleDelta.x === 0)
{
if(factor > 1)
flick(pageView.scrollSpeed);
else
flick(-pageView.scrollSpeed);
}
}
// Calculate the current page and update the document.
function updateCurrentPageCounter()
{
// A new page starts if it is over the middle of the screen (vertically).
let pageHeight = Math.round(pageView.defaultPageHeight * pageView.zoomFactor);
let currentPos = pageView.contentY - pageView.originY + pageView.height/2;
let pageNumber = Math.floor(currentPos / pageHeight);
if(pageNumber !== root.document.currentPage)
root.document.currentPage = pageNumber;
}
/**
Changes the current move direction of the listview, without actually
moving visibly. This is neccessary since the listview only chaches
delegates in the direction of the current move direction.
If we e.g. scroll downwards and then go to the previousPage
by setting the contentY, the previous pages are not cached
which might lead to visible loading while moving through the
book with the arrow keys.
*/
function setMoveDirection(direction)
{
if(direction === "up")
{
flick(-1000);
pageView.cancelFlick();
}
else if(direction === "down")
{
flick(1000);
pageView.cancelFlick();
}
}
function zoom(factor)
{
let newZoomFactor = pageView.zoomFactor * factor;
let newPageHeight = Math.round(pageView.defaultPageHeight * newZoomFactor);
if (newPageHeight < pageView.defaultPageHeight / 5 || newPageHeight > pageView.defaultPageHeight * 2.5)
return;
let currentPageHeight = Math.round(pageView.defaultPageHeight * pageView.zoomFactor);
let currentPageNumber = root.document.currentPage;
let currentPos = pageView.contentY - pageView.originY;
let pageOffset = currentPos - (currentPageHeight * currentPageNumber);
pageView.zoomFactor = newZoomFactor;
pageView.forceLayout();
pageView.contentY = newPageHeight * currentPageNumber + pageOffset + pageView.originY;
}
function flick(factor)
{
pageView.flick(0, factor);
}
function setPage(newPageNumber)
{
let pageHeight = Math.round(pageView.defaultPageHeight * pageView.zoomFactor);
let newContentY = (pageHeight * newPageNumber) + pageView.originY;
if(newPageNumber > root.document.currentPage)
setMoveDirection("up");
else if(newPageNumber < root.document.currentPage)
setMoveDirection("down");
pageView.contentY = newContentY;
}
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(u"qrc:/ZoomingTest/Main.qml"_qs);
QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
&app, []() { QCoreApplication::exit(-1); },
Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
import "DocumentNavigation.js" as NavigationLogic
/*
A view on the document's pages in a certain layout (e.g. vertical)
*/
ApplicationWindow
{
id: root
visible: true
visibility: Window.Maximized
property int currentPage: 4
MouseArea
{
id: mouseArea
anchors.fill: parent
// Handle scrolling customly
onWheel: (wheel) => NavigationLogic.handleWheel(wheel)
ListView
{
id: pageView
readonly property real defaultPageHeight: 1334
property real zoomFactor: 1
readonly property int defaultPageSpacing: 12
readonly property int scrollSpeed: 1600
height: parent.height
width: currentItem.width <= root.width ? currentItem.width : root.width
contentWidth: currentItem.width
anchors.centerIn: parent
flickableDirection: Flickable.AutoFlickDirection
interactive: true
clip: true
cacheBuffer: 2000
boundsMovement: Flickable.StopAtBounds
flickDeceleration: 10000
model: 500
delegate: Rectangle
{
height: Math.round(pageView.defaultPageHeight * pageView.zoomFactor)
width: height * 0.7
color: modelData % 2 == 0 ? "gray" : "green"
Label
{
anchors.centerIn: parent
font.bold: true
font.pointSize: 20
text: modelData
}
}
onContentYChanged: NavigationLogic.updateCurrentPageCounter();
MouseArea
{
id: wheelInterceptor
anchors.fill: parent
onWheel: (wheel) =>
{
NavigationLogic.handleWheel(wheel);
wheel.accepted = true;
}
}
}
}
function zoom(factor)
{
NavigationLogic.zoom(factor);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment