diff --git a/.dockerignore b/.dockerignore index 72e8ffc0db..329ee73f78 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,3 @@ * +!.github/ +!.github/qt_helper.py diff --git a/.github/qt_helper.py b/.github/qt_helper.py new file mode 100644 index 0000000000..8dd159b2fa --- /dev/null +++ b/.github/qt_helper.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 +import argparse +import fnmatch +import hashlib +import pathlib +import subprocess +import urllib.parse +import urllib.request +import xml.etree.ElementTree as ET + +MAX_TRIES = 32 +MAX_XML_SIZE = 1024 * 1024 * 1024 +MIRROR = 'download.qt.io' + + +def fetch_links_to_archives(host_os, target, major, minor, patch, toolchain, packages): + qt_dir = f'qt{major}_{major}{minor}{patch}' + base_url = f'https://{MIRROR}/online/qtsdkrepository/{host_os}/{target}/{qt_dir}/{qt_dir}' + url = f'{base_url}/Updates.xml' + print('fetching', url, flush=True) + + for _ in range(MAX_TRIES): + try: + with urllib.request.urlopen(url, timeout=30) as response: + resp = response.read(MAX_XML_SIZE + 1) + if len(resp) > MAX_XML_SIZE: + raise RuntimeError(f'{url} exceeds the {MAX_XML_SIZE}-byte size limit') + update_xml = ET.fromstring(resp) + break + except KeyboardInterrupt: + raise + except Exception as e: + print('error', e, flush=True) + else: + raise RuntimeError(f'Failed to fetch {url} after {MAX_TRIES} attempts') + + package_prefix = f'qt.qt{major}.{major}{minor}{patch}' + package_names = { + f'{package_prefix}.{package}.{toolchain}' if package else f'{package_prefix}.{toolchain}' + for package in packages + } + + found_packages = set() + for pkg in update_xml.findall('./PackageUpdate'): + name = pkg.find('.//Name') + if name is None: + continue + if name.text not in package_names: + continue + found_packages.add(name.text) + version = pkg.find('.//Version') + if version is None: + continue + archives = pkg.find('.//DownloadableArchives') + if archives is None or archives.text is None: + continue + for archive in archives.text.split(', '): + archive = archive.strip() + if not archive: + continue + url = f'{base_url}/{name.text}/{version.text}{archive}' + file_name = pathlib.Path(urllib.parse.urlparse(url).path).name + yield {'name': file_name, 'url': url, 'archive': archive} + + missing_packages = package_names - found_packages + if missing_packages: + raise RuntimeError(f'Qt packages not found: {", ".join(sorted(missing_packages))}') + + +def download(links): + metalink = ET.Element('metalink', xmlns='urn:ietf:params:xml:ns:metalink') + for link in links: + file = ET.SubElement(metalink, 'file', name=link['name']) + ET.SubElement(file, 'url').text = link['url'] + + data = ET.tostring(metalink, encoding='UTF-8', xml_declaration=True) + + for _ in range(MAX_TRIES): + result = subprocess.run([ + 'aria2c', + '--connect-timeout=8', + '--console-log-level=warn', + '--continue', + '--follow-metalink=mem', + '--max-concurrent-downloads=100', + '--max-connection-per-server=16', + '--max-file-not-found=100', + '--max-tries=100', + '--min-split-size=1MB', + '--retry-wait=1', + '--split=100', + '--summary-interval=0', + '--timeout=8', + '--user-agent=', + '--metalink-file=-', + ], input=data, check=False) + if result.returncode == 0: + return True + + return False + + +def file_hash(path): + digest = hashlib.sha256() + with open(path, 'rb') as file: + for chunk in iter(lambda: file.read(1024 * 1024), b''): + digest.update(chunk) + return digest.digest() + + +def calc_hash_sum(files): + digest = hashlib.sha256() + for path in files: + digest.update(file_hash(path)) + return digest.hexdigest() + + +def extract_archives(files, out='.', targets=()): + for path in files: + print('extracting', path, flush=True) + result = subprocess.run( + ['bsdtar', '-xf', path, '-C', out, *targets], + stdout=subprocess.DEVNULL, + check=False, + ) + if result.returncode != 0: + return False + return True + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('os') + parser.add_argument('target') + parser.add_argument('version') + parser.add_argument('toolchain') + parser.add_argument('expect') + parser.add_argument('--add-package', action='append', default=[], + help='additional package below qt.qt., such as addons.qtshadertools') + parser.add_argument('--archive', action='append', default=[], + help='fnmatch pattern selecting archives from the requested packages') + args = parser.parse_args() + + host_os, target, version, toolchain, expect = ( + args.os, args.target, args.version, args.toolchain, args.expect + ) + major, minor, patch = version.split('.') + + packages = [''] + args.add_package + links = list(fetch_links_to_archives( + host_os, target, major, minor, patch, toolchain, packages + )) + if args.archive: + links = [ + link for link in links + if any(fnmatch.fnmatch(link['archive'], pattern) for pattern in args.archive) + ] + if not links: + raise RuntimeError('No Qt archives matched') + print(*(link['url'] for link in links), sep='\n', flush=True) + + if not download(links): + raise RuntimeError('Failed to download Qt archives') + + archive_names = [link['name'] for link in links] + result = calc_hash_sum(archive_names) + print('result', result, 'expect', expect, flush=True) + if expect != '-' and result != expect: + raise RuntimeError(f'Qt archive hash mismatch: expected {expect}, got {result}') + + if not extract_archives(archive_names): + raise RuntimeError('Failed to extract Qt archives') + + for archive_name in archive_names: + pathlib.Path(archive_name).unlink() + + +if __name__ == '__main__': + main() diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 722b3fa04d..f41173a552 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,28 +4,26 @@ on: [push, pull_request] env: FREE_DISKSPACE: | - sudo rm -rf /usr/local/.ghcup /usr/share/dotnet /usr/share/swift /usr/share/miniconda - QT_TAG: v5.15.17-lts-lgpl - QT_PREFIX: ${{ github.workspace }}/qt-install + sudo rm -rf /usr/local/.ghcup /usr/share/dotnet /usr/share/swift /usr/share/miniconda /usr/local/lib/android /opt/hostedtoolcache jobs: build-macos: runs-on: macos-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v7 with: submodules: recursive - name: install dependencies - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install boost hidapi ninja openssl zmq libsodium unbound protobuf qt5 pkg-config + run: HOMEBREW_NO_AUTO_UPDATE=1 brew install boost hidapi ninja openssl zmq libsodium unbound protobuf qt pkg-config - name: build run: DEV_MODE=ON make release - name: test qml run: build/release/bin/monero-wallet-gui.app/Contents/MacOS/monero-wallet-gui --test-qml build-ubuntu: - runs-on: ubuntu-latest + runs-on: ubuntu-26.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v7 with: submodules: recursive - name: remove bundled boost @@ -38,13 +36,14 @@ jobs: - name: update apt run: sudo apt update - name: install monero dependencies - run: sudo apt -y install build-essential cmake ninja-build libboost-all-dev libunbound-dev graphviz doxygen pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev libprotobuf-dev protobuf-compiler - - name: install monero gui dependencies - run: sudo apt -y install qtbase5-dev qtdeclarative5-dev qml-module-qtqml-models2 qml-module-qtquick-controls qml-module-qtquick-controls2 qml-module-qtquick-dialogs qml-module-qtquick-xmllistmodel qml-module-qt-labs-settings qml-module-qt-labs-platform qml-module-qt-labs-folderlistmodel qml-module-qttest qttools5-dev-tools qml-module-qtquick-templates2 libqt5svg5-dev libgcrypt20-dev xvfb + run: sudo apt -y install build-essential cmake ninja-build libboost-all-dev libunbound-dev graphviz doxygen pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev libprotobuf-dev protobuf-compiler libgcrypt20-dev qt6-base-dev qt6-declarative-dev qt6-svg-dev qt6-tools-dev qt6-tools-dev-tools qml6-module-qtcore qml6-module-qtqml qml6-module-qtqml-models qml6-module-qtquick qml6-module-qtquick-controls qml6-module-qtquick-dialogs qml6-module-qtquick-effects qml6-module-qtquick-layouts qml6-module-qtquick-shapes qml6-module-qtquick-window qml6-module-qt-labs-folderlistmodel qml6-module-qt-labs-platform qml6-module-qttest - name: build run: DEV_MODE=ON make release - name: test qml - run: xvfb-run -a build/release/bin/monero-wallet-gui --test-qml + env: + QT_QPA_PLATFORM: offscreen + QT_QUICK_BACKEND: software + run: build/release/bin/monero-wallet-gui --test-qml build-windows: runs-on: windows-latest @@ -52,7 +51,7 @@ jobs: run: shell: msys2 {0} steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v7 with: submodules: recursive - uses: eine/setup-msys2@v2 @@ -60,11 +59,9 @@ jobs: msystem: ucrt64 update: true install: make - pacboy: toolchain:p pcre:p cmake:p ninja:p boost:p openssl:p zeromq:p libsodium:p hidapi:p protobuf-c:p libusb:p unbound:p git qt5:p libgcrypt:p angleproject:p - - name: add qmake.exe and windeployqt.exe - run: | - cp -f "$MSYSTEM_PREFIX/bin/qmake-qt5.exe" "$MSYSTEM_PREFIX/bin/qmake.exe" - cp -f "$MSYSTEM_PREFIX/bin/windeployqt-qt5.exe" "$MSYSTEM_PREFIX/bin/windeployqt.exe" + pacboy: toolchain:p pcre:p cmake:p ninja:p boost:p openssl:p zeromq:p libsodium:p hidapi:p protobuf:p libusb:p unbound:p git qt6-base:p qt6-declarative:p qt6-svg:p qt6-tools:p libgcrypt:p + - name: add qmlimportscanner.exe + run: cp -f "$MSYSTEM_PREFIX/share/qt6/bin/qmlimportscanner.exe" "$MSYSTEM_PREFIX/bin/qmlimportscanner.exe" - name: build run: DEV_MODE=ON make release-win64 - name: deploy @@ -74,88 +71,86 @@ jobs: run: build/release/bin/monero-wallet-gui --test-qml -o test.log,txt || { rc=$?; cat test.log; exit "$rc"; } macos-bundle: - runs-on: macos-15 + name: macOS ${{ matrix.arch }} bundle + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - runner: macos-14 + archive_name: monero-wallet-gui-macos-armv8.tar.gz + arch: ARM + - runner: macos-15-intel + archive_name: monero-wallet-gui-macos-x64.tar.gz + arch: Intel steps: - - uses: actions/checkout@v1 - with: - submodules: recursive - - name: install dependencies - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install boost hidapi ninja openssl zmq unbound protobuf pkg-config - - name: clone qt repo - run: git clone -b "${QT_TAG}" --recursive --depth 1 --shallow-submodules https://github.com/qt/qt5 - - name: build qt from source - run: | - cd qt5 - mkdir build && cd build - ../configure -prefix "${QT_PREFIX}" -opensource -confirm-license -release -nomake examples -nomake tests -no-rpath -skip qtwebengine -skip qt3d -skip qtandroidextras -skip qtcanvas3d -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdoc -skip qtgamepad -skip qtlocation -skip qtnetworkauth -skip qtpurchasing -skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qttools -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebsockets -skip qtwebview -skip qtwinextras -skip qtx11extras -skip gamepad -skip serialbus -skip location -skip webengine - make -j"$(sysctl -n hw.ncpu)" - make install - cd ../qttools/src/linguist/lrelease - ../../../../build/qtbase/bin/qmake - make -j"$(sysctl -n hw.ncpu)" - make install - cd ../../../../qttools/src/macdeployqt/macdeployqt/ - ../../../../build/qtbase/bin/qmake - make -j"$(sysctl -n hw.ncpu)" - make install - - name: build monero-gui - run: | - mkdir build && cd build - cmake -G Ninja -D CMAKE_BUILD_TYPE=Release -D ARCH=default -D CMAKE_PREFIX_PATH="${QT_PREFIX}" .. - cmake --build . - - name: deploy - run: cmake --build . --target deploy - working-directory: build - - name: test qml - run: build/bin/monero-wallet-gui.app/Contents/MacOS/monero-wallet-gui --test-qml - - name: create .tar - run: tar -cf monero-wallet-gui.tar monero-wallet-gui.app - working-directory: build/bin - - uses: actions/upload-artifact@v4 - with: - name: ${{ github.job }} - path: build/bin/monero-wallet-gui.tar + - uses: actions/checkout@v7 + with: + submodules: recursive + - name: install dependencies + run: HOMEBREW_NO_AUTO_UPDATE=1 brew install boost hidapi libsodium ninja openssl pkg-config protobuf unbound zeromq aria2 + - name: download Qt + run: mkdir qt6.8.3 && cd qt6.8.3 && python3 ../monero-gui/.github/qt_helper.py mac_x64 desktop 6.8.3 clang_64 c9cdbe2ad3d34cf6e675a7e6eaf5a16560a51305dd06aed97b8d93359b1b90fa + working-directory: ../ + - name: configure + run: > + cmake -S . -B build -G Ninja + -D CMAKE_PREFIX_PATH="/Users/runner/work/monero-gui/qt6.8.3/" + -D CMAKE_OSX_DEPLOYMENT_TARGET="12.0" + -D ARCH=default + - name: build + run: cmake --build build + - name: deploy + run: cmake --build build --target deploy + - name: test qml + run: build/bin/monero-wallet-gui.app/Contents/MacOS/monero-wallet-gui --test-qml + - name: create archive + run: tar -C build/bin -czf "${{ matrix.archive_name }}" monero-wallet-gui.app + - uses: actions/upload-artifact@v7 + with: + path: ${{ matrix.archive_name }} + archive: false docker-linux-static: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v7 with: submodules: recursive - - name: install dependencies - run: sudo apt -y install xvfb libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xkb1 libxcb-shape0 libxkbcommon-x11-0 - name: free up diskspace run: ${{env.FREE_DISKSPACE}} - name: prepare build environment - run: docker build --tag monero:build-env-linux --build-arg THREADS=3 --file Dockerfile.linux . + run: docker build --tag monero:build-env-linux --build-arg THREADS=4 --file Dockerfile.linux . - name: build - run: docker run --rm -v /home/runner/work/monero-gui/monero-gui:/monero-gui -w /monero-gui monero:build-env-linux sh -c 'make release-static' + run: docker run --rm -v /home/runner/work/monero-gui/monero-gui:/monero-gui -w /monero-gui monero:build-env-linux sh -c 'make depends root=/depends target=x86_64-linux-gnu tag=linux-x64' - name: sha256sum - run: shasum -a256 /home/runner/work/monero-gui/monero-gui/build/release/bin/monero-wallet-gui + run: shasum -a256 /home/runner/work/monero-gui/monero-gui/build/x86_64-linux-gnu/release/bin/monero-wallet-gui + - name: install test dependencies + run: sudo apt -y install xvfb libopengl0 libegl1 libgl1 libxkbcommon-x11-0 libxcb-cursor0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-shape0 - name: test qml - run: xvfb-run -a /home/runner/work/monero-gui/monero-gui/build/release/bin/monero-wallet-gui --test-qml - - uses: actions/upload-artifact@v4 + run: xvfb-run -a /home/runner/work/monero-gui/monero-gui/build/x86_64-linux-gnu/release/bin/monero-wallet-gui --test-qml + - uses: actions/upload-artifact@v7 with: name: ${{ github.job }} path: | - /home/runner/work/monero-gui/monero-gui/build/release/bin/monero-wallet-gui - /home/runner/work/monero-gui/monero-gui/build/release/bin/monerod + /home/runner/work/monero-gui/monero-gui/build/x86_64-linux-gnu/release/bin/monero-wallet-gui + /home/runner/work/monero-gui/monero-gui/build/x86_64-linux-gnu/release/bin/monerod docker-windows-static: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v7 with: submodules: recursive - name: free up diskspace run: ${{env.FREE_DISKSPACE}} - name: prepare build environment - run: docker build --tag monero:build-env-windows --build-arg THREADS=3 --file Dockerfile.windows . + run: docker build --tag monero:build-env-windows --build-arg THREADS=4 --file Dockerfile.windows . - name: build run: docker run --rm -v /home/runner/work/monero-gui/monero-gui:/monero-gui -w /monero-gui monero:build-env-windows sh -c 'make depends root=/depends target=x86_64-w64-mingw32 tag=win-x64' - name: sha256sum run: shasum -a256 /home/runner/work/monero-gui/monero-gui/build/x86_64-w64-mingw32/release/bin/monero-wallet-gui.exe - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: ${{ github.job }} path: | @@ -165,24 +160,24 @@ jobs: docker-android: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v7 with: submodules: recursive - name: free up diskspace run: ${{env.FREE_DISKSPACE}} - name: prepare build environment - run: docker build --tag monero:build-env-android --build-arg THREADS=3 --file Dockerfile.android . + run: docker build --tag monero:build-env-android --build-arg THREADS=4 --file Dockerfile.android . - name: build run: docker run --rm -v /home/runner/work/monero-gui/monero-gui:/monero-gui monero:build-env-android - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: ${{ github.job }} - path: /home/runner/work/monero-gui/monero-gui/build/Android/release/android-build/monero-gui.apk + path: /home/runner/work/monero-gui/monero-gui/build/Android/release/android-build/build/outputs/apk/release/android-build-release-unsigned.apk source-archive: runs-on: ubuntu-slim steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v7 with: submodules: recursive - name: archive @@ -192,7 +187,7 @@ jobs: export OUTPUT="$VERSION.tar" echo "OUTPUT=$OUTPUT" >> $GITHUB_ENV /home/runner/.local/bin/git-archive-all --prefix "$VERSION/" --force-submodules "$OUTPUT" - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: ${{ env.OUTPUT }} path: /home/runner/work/monero-gui/monero-gui/${{ env.OUTPUT }} diff --git a/.github/workflows/verify_p2pool.yml b/.github/workflows/verify_p2pool.yml index 2afa1fb790..8ae042d55b 100644 --- a/.github/workflows/verify_p2pool.yml +++ b/.github/workflows/verify_p2pool.yml @@ -10,7 +10,7 @@ jobs: p2pool-hashes: runs-on: ubuntu-slim steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v7 - name: Verify Hashes run: | python3 .github/verify_p2pool.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 7507e80c57..7635f924d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.12) +cmake_minimum_required(VERSION 3.22) project(monero-gui) include(CMakeDependentOption) @@ -19,12 +19,7 @@ option(WITH_UPDATER "Regularly check for new updates" ON) option(DEV_MODE "Checkout latest monero master on build" OFF) cmake_dependent_option(QML_TESTS "Build QML tests" ON "NOT STATIC;NOT ANDROID;NOT IOS" OFF) -if(DEV_MODE) - # DEV_MODE checks out the monero submodule to master, which requires C++17. - set(CMAKE_CXX_STANDARD 17) -else() - set(CMAKE_CXX_STANDARD 14) -endif() +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake") @@ -116,219 +111,53 @@ if(UNIX AND NOT APPLE AND NOT ANDROID) endif() endif() -set(QT5_LIBRARIES - Qt5Core - Qt5Quick - Qt5Gui - Qt5Qml - Qt5Svg - Qt5Xml -) - -if(QML_TESTS) - list(APPEND QT5_LIBRARIES Qt5QuickTest Qt5Test) -endif() - -if(WITH_SCANNER) - list(APPEND QT5_LIBRARIES Qt5Multimedia) -endif() - -if(APPLE) - list(APPEND QT5_LIBRARIES Qt5MacExtras) -endif() - if(UNIX) if(NOT CMAKE_PREFIX_PATH AND DEFINED ENV{CMAKE_PREFIX_PATH}) message(STATUS "Using CMAKE_PREFIX_PATH environment variable: '$ENV{CMAKE_PREFIX_PATH}'") set(CMAKE_PREFIX_PATH $ENV{CMAKE_PREFIX_PATH}) endif() if(APPLE AND NOT CMAKE_PREFIX_PATH) - execute_process(COMMAND brew --prefix qt5 OUTPUT_VARIABLE QT5_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) - list(APPEND CMAKE_PREFIX_PATH ${QT5_DIR}) + execute_process(COMMAND brew --prefix qt OUTPUT_VARIABLE QT6_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) + list(APPEND CMAKE_PREFIX_PATH ${QT6_DIR}) endif() endif() -set(QT_MIN_VERSION "5.12") +set(QT_MIN_VERSION "6.8") -find_package(PkgConfig REQUIRED) - -# TODO: drop this once we switch to Qt 5.14+ -pkg_check_modules(Qt5QmlModels_PKG_CONFIG QUIET Qt5QmlModels) -if(Qt5QmlModels_PKG_CONFIG_FOUND) - list(APPEND QT5_LIBRARIES Qt5QmlModels) +set(QT_COMPONENTS + Core + Concurrent + Gui + Qml + QmlModels + Quick + QuickControls2 + Svg + Widgets + Xml +) +if(QML_TESTS) + list(APPEND QT_COMPONENTS QuickTest Test) endif() - -foreach(QT5_MODULE ${QT5_LIBRARIES}) - find_package(${QT5_MODULE} ${QT_MIN_VERSION} REQUIRED) - include_directories(${${QT5_MODULE}_INCLUDE_DIRS}) -endforeach() - -if(NOT (CMAKE_CROSSCOMPILING AND ANDROID)) - pkg_check_modules(QT5_PKG_CONFIG REQUIRED ${QT5_LIBRARIES}) -else() - set(QT5_LIBRARIES_ABI) - foreach(QT5_MODULE ${QT5_LIBRARIES}) - list(APPEND QT5_LIBRARIES_ABI "${QT5_MODULE}_${CMAKE_ANDROID_ARCH_ABI}") - endforeach() - pkg_check_modules(QT5_PKG_CONFIG REQUIRED ${QT5_LIBRARIES_ABI}) +if(WITH_SCANNER) + list(APPEND QT_COMPONENTS Multimedia) endif() +find_package(Qt6 ${QT_MIN_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS}) -get_target_property(QMAKE_IMPORTED_LOCATION Qt5::qmake IMPORTED_LOCATION) +list(TRANSFORM QT_COMPONENTS PREPEND "Qt6::" OUTPUT_VARIABLE QT_LIBRARIES) + +get_target_property(QMAKE_IMPORTED_LOCATION Qt6::qmake IMPORTED_LOCATION) get_filename_component(QT_INSTALL_PREFIX "${QMAKE_IMPORTED_LOCATION}/../.." ABSOLUTE) if(APPLE AND NOT STATIC) set(CMAKE_BUILD_RPATH "${QT_INSTALL_PREFIX}/lib") endif() -if(QT5_PKG_CONFIG_FOUND) - set(QT5_PKG_CONFIG "QT5_PKG_CONFIG") - if(STATIC) - set(QT5_PKG_CONFIG "${QT5_PKG_CONFIG}_STATIC") - endif() - - if(UNIX AND CMAKE_PREFIX_PATH) - if(APPLE) - list(JOIN ${QT5_PKG_CONFIG}_LDFLAGS_OTHER " " ${QT5_PKG_CONFIG}_LDFLAGS_OTHER) - endif() - # temporal workaround for https://bugreports.qt.io/browse/QTBUG-80922 - STRING(REPLACE "${QT5_PKG_CONFIG_Qt5Core_PREFIX}" "${QT_INSTALL_PREFIX}" ${QT5_PKG_CONFIG}_LDFLAGS_OTHER "${${QT5_PKG_CONFIG}_LDFLAGS_OTHER}") - STRING(REPLACE "${QT5_PKG_CONFIG_Qt5Core_PREFIX}" "${QT_INSTALL_PREFIX}" ${QT5_PKG_CONFIG}_LIBRARIES "${${QT5_PKG_CONFIG}_LIBRARIES}") - STRING(REPLACE "${QT5_PKG_CONFIG_Qt5Core_PREFIX}" "${QT_INSTALL_PREFIX}" ${QT5_PKG_CONFIG}_INCLUDE_DIRS "${${QT5_PKG_CONFIG}_INCLUDE_DIRS}") - STRING(REPLACE "${QT5_PKG_CONFIG_Qt5Core_PREFIX}" "${QT_INSTALL_PREFIX}" ${QT5_PKG_CONFIG}_LIBRARY_DIRS "${${QT5_PKG_CONFIG}_LIBRARY_DIRS}") - endif() - - set(QT5_LIBRARIES ${${QT5_PKG_CONFIG}_LIBRARIES} ${${QT5_PKG_CONFIG}_LDFLAGS_OTHER}) - include_directories(${${QT5_PKG_CONFIG}_INCLUDE_DIRS}) - link_directories(${${QT5_PKG_CONFIG}_LIBRARY_DIRS}) -endif() - -list(APPEND QT5_LIBRARIES - ${Qt5Gui_PLUGINS} - ${Qt5Svg_PLUGINS} - ${Qt5Qml_PLUGINS} - ${Qt5Network_PLUGINS} -) - -if(STATIC) - set(QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/Qt/labs/folderlistmodel) - list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/Qt/labs/settings) - list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/Qt/labs/platform) - list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtGraphicalEffects) - list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtGraphicalEffects/private) - list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtMultimedia) - list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQml) - list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQml/Models.2) - list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick.2) - list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Controls) - list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Controls.2) - list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Dialogs) - list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Dialogs/Private) - list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Layouts) - list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/PrivateWidgets) - list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Templates.2) - list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Window.2) - - set(QT5_EXTRA_LIBRARIES_LIST - qtquicktemplates2plugin - Qt5QuickTemplates2 - qtquickcontrols2plugin - Qt5QuickControls2 - dialogplugin - dialogsprivateplugin - qmlfolderlistmodelplugin - qmlsettingsplugin - qtlabsplatformplugin - qquicklayoutsplugin - modelsplugin - ) - - if(WITH_SCANNER) - list(APPEND QT5_EXTRA_LIBRARIES_LIST - declarative_multimedia - Qt5MultimediaQuick - ) - endif() - - list(APPEND QT5_EXTRA_LIBRARIES_LIST - qtgraphicaleffectsplugin - qtgraphicaleffectsprivate - qtquick2plugin - qtquickcontrolsplugin - widgetsplugin - windowplugin - ) - - if(NOT ${Qt5Core_VERSION} VERSION_LESS 5.14) - list(APPEND QT5_EXTRA_LIBRARIES_LIST qmlplugin) - endif() - - set(QT5_EXTRA_LIBRARIES) - foreach(LIBRARY ${QT5_EXTRA_LIBRARIES_LIST}) - find_library(${LIBRARY}_LIBRARY ${LIBRARY} PATHS ${QT5_EXTRA_PATHS} REQUIRED) - list(APPEND QT5_EXTRA_LIBRARIES ${${LIBRARY}_LIBRARY}) - endforeach() - - if(MINGW) - if(CMAKE_BUILD_TYPE STREQUAL "Debug") - list(APPEND QT5_EXTRA_LIBRARIES D3D11 Dwrite D2d1) - endif() - endif() - - set(QT5_LIBRARIES - ${QT5_EXTRA_LIBRARIES} - ${QT5_LIBRARIES} - ) - - set(QT5_INTEGRATION_LIBRARIES_LIST - Qt5EventDispatcherSupport - Qt5PacketProtocol - Qt5ThemeSupport - Qt5FontDatabaseSupport - ) - - if(UNIX AND NOT APPLE) - list(APPEND QT5_INTEGRATION_LIBRARIES_LIST - Qt5XcbQpa - Qt5ServiceSupport - Qt5GlxSupport - ) - elseif(MINGW) - list(APPEND QT5_INTEGRATION_LIBRARIES_LIST qtfreetype) - endif() - - foreach(LIBRARY ${QT5_INTEGRATION_LIBRARIES_LIST}) - find_library(${LIBRARY}_LIBRARY ${LIBRARY} PATHS ${QT5_EXTRA_PATHS} REQUIRED) - list(APPEND QT5_LIBRARIES ${${LIBRARY}_LIBRARY}) - endforeach() - - if(UNIX AND NOT APPLE) - pkg_check_modules(X11XCB_XCBGLX REQUIRED x11-xcb xcb-glx) - list(APPEND QT5_LIBRARIES ${X11XCB_XCBGLX_LIBRARIES}) - pkg_check_modules(FONTCONFIG REQUIRED fontconfig) - list(APPEND QT5_LIBRARIES ${FONTCONFIG_STATIC_LIBRARIES}) - endif() -endif() - -if(ANDROID) - set(QT5_EXTRA_LIBRARIES_LIST - GLESv2 - log - z - jnigraphics - android - EGL - Qt5VirtualKeyboard_${CMAKE_ANDROID_ARCH_ABI} - c++_shared - ) - foreach(LIBRARY ${QT5_EXTRA_LIBRARIES_LIST}) - find_library(${LIBRARY}_LIBRARY ${LIBRARY} PATHS "${ANDROID_TOOLCHAIN_ROOT}/sysroot/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}/${ANDROID_PLATFORM_LEVEL}" REQUIRED) - list(APPEND QT5_LIBRARIES ${${LIBRARY}_LIBRARY}) - endforeach() -endif() - if(MINGW) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wa,-mbig-obj") - set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi;crypt32;bcrypt) + # QtCore uses the Windows version-information APIs, but Qt 6.8 does not + # propagate version.lib to consumers. + set(EXTRA_LIBRARIES mswsock ws2_32 iphlpapi crypt32 bcrypt -lversion) if(DEPENDS) set(ICU_LIBRARIES icuio icui18n icuuc icudata icutu iconv) else() @@ -447,4 +276,80 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_SECURITY_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS} ${STATIC_FLAGS}") add_subdirectory(translations) + +set(EXECUTABLE_FLAG) +if(MINGW) + set(EXECUTABLE_FLAG WIN32) + set(ICON "${PROJECT_SOURCE_DIR}/images/appicon.ico") + set(ICON_RC "${CMAKE_CURRENT_BINARY_DIR}/icon.rc") + set(ICON_RES "${CMAKE_CURRENT_BINARY_DIR}/icon.o") + file(WRITE "${ICON_RC}" "IDI_ICON1 ICON DISCARDABLE \"${ICON}\"") + find_program(WINDRES_EXECUTABLE + NAMES windres x86_64-w64-mingw32-windres + REQUIRED + CMAKE_FIND_ROOT_PATH_BOTH + ) + add_custom_command( + OUTPUT "${ICON_RES}" + COMMAND "${WINDRES_EXECUTABLE}" "${ICON_RC}" "${ICON_RES}" + MAIN_DEPENDENCY "${ICON_RC}" + ) +endif() + +if(APPLE) + set(ICON "${PROJECT_SOURCE_DIR}/images/appicon.icns") + set_source_files_properties("${ICON}" PROPERTIES + MACOSX_PACKAGE_LOCATION "Resources" + ) +endif() + +if(NOT ANDROID) + add_executable(monero-wallet-gui ${EXECUTABLE_FLAG} ${ICON} ${ICON_RES}) +else() + qt_add_executable(monero-wallet-gui) + target_compile_definitions(monero-wallet-gui PRIVATE ANDROID) +endif() + add_subdirectory(src) + +include(src/qml-resources.cmake) + +qt_policy(SET QTP0004 NEW) + +set(QML_MODULE_OPTIONS) +if(MINGW AND NOT CMAKE_GENERATOR MATCHES "Ninja") + message(STATUS "Disabling QML cache generation: MinGW requires the Ninja generator") + list(APPEND QML_MODULE_OPTIONS NO_CACHEGEN) +endif() + +qt_add_qml_module(monero-wallet-gui + ${QML_MODULE_OPTIONS} + URI MoneroGUI + VERSION 1.0 + RESOURCE_PREFIX / + NO_RESOURCE_TARGET_PATH + NO_GENERATE_EXTRA_QMLDIRS + IMPORT_PATH + "${CMAKE_SOURCE_DIR}/fonts" + QML_FILES + ${GUI_QML_FILES} + RESOURCES + ${GUI_ASSET_FILES} +) + +if(STATIC) + qt_import_qml_plugins(monero-wallet-gui) +endif() + +if(APPLE AND WITH_SCANNER) + qt_import_plugins(monero-wallet-gui INCLUDE Qt6::QDarwinCameraPermissionPlugin) + target_link_options(monero-wallet-gui PRIVATE "-Wl,-u,_QDarwinCameraPermissionRequest") +endif() + +add_custom_command(TARGET monero-wallet-gui POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + $ + $ +) + +install(TARGETS monero-wallet-gui DESTINATION bin) diff --git a/DEPLOY.md b/DEPLOY.md index 660cd2c2a0..c67fefefbc 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -1,6 +1,6 @@ # macOS: -Use macOS 10.12 - 10.13 for better backwards compability. +Use macOS 12 for better backwards compability. 1. `HOMEBREW_OPTFLAGS="-march=core2" HOMEBREW_OPTIMIZATION_LEVEL="O0" brew install boost zmq libpgm libsodium expat protobuf@21 libgcrypt hidapi libusb cmake pkg-config && brew link protobuf@21` @@ -12,9 +12,9 @@ Use macOS 10.12 - 10.13 for better backwards compability. ```bash mkdir build && cd build -cmake -D CMAKE_BUILD_TYPE=Release -D ARCH=default -D CMAKE_PREFIX_PATH=/path/to/Qt5.12.8/5.12.8/clang_64 .. -make -make deploy +cmake -S . -B build -G Ninja -D ARCH=default -D CMAKE_PREFIX_PATH=/path/to/Qt6.8.3/ +cmake --build build +cmake --build build --target deploy ``` 5. Replace the `monerod` binary inside `monero-wallet-gui.app/Contents/MacOS/` with one built using deterministic builds / gitian. @@ -45,31 +45,3 @@ You can check if this step worked by using `codesign -dvvv monero-wallet-gui.app 5. `xcrun notarytool info aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeee --apple-id email@address.org --team-id XXXXXXXXXX` 6. `xcrun stapler staple -v monero-gui-mac-x64-v0.X.Y.Z.dmg` - -## Compile Qt for Apple Silicon - -Qt does not offer pre-built binaries for Apple Silicon, they have to be manually compiled. - -```bash -git clone https://github.com/qt/qt5.git -cd qt5 -git checkout v5.15.9-lts-lgpl -./init-repository -mkdir build -cd build -../configure -prefix /path/to/qt-build-dir/ -opensource -confirm-license -release -nomake examples -nomake tests -no-rpath -skip qtwebengine -skip qt3d -skip qtandroidextras -skip qtcanvas3d -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdoc -skip qtgamepad -skip qtlocation -skip qtnetworkauth -skip qtpurchasing -skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qttools -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebsockets -skip qtwebview -skip qtwinextras -skip qtx11extras -skip gamepad -skip serialbus -skip location -skip webengine -make -make install -cd ../qttools/src/linguist/lrelease -../../../../build/qtbase/bin/qmake -make -make install -cd ../../../../qttools/src/macdeployqt/macdeployqt/ -../../../../build/qtbase/bin/qmake -make -make install -``` - -For compilation with Xcode 15 the following patch has to be applied: https://raw.githubusercontent.com/Homebrew/formula-patches/086e8cf/qt5/qt5-qmake-xcode15.patch - -The `CMAKE_PREFIX_PATH` has to be set to `/path/to/qt-build-dir/` during monero-gui compilation. diff --git a/Dockerfile.android b/Dockerfile.android index 4dde3ddc74..837d4ac8f3 100644 --- a/Dockerfile.android +++ b/Dockerfile.android @@ -1,31 +1,38 @@ -FROM ubuntu:20.04 +FROM debian:bookworm ARG THREADS=1 -ARG ANDROID_NDK_REVISION=23c -ARG ANDROID_NDK_HASH=e5053c126a47e84726d9f7173a04686a71f9a67a -ARG ANDROID_SDK_REVISION=7302050_latest -ARG ANDROID_SDK_HASH=7a00faadc0864f78edd8f4908a629a46d622375cbe2e5814e82934aebecdb622 -ARG QT_VERSION=v5.15.19-lts-lgpl +ARG QT_VERSION=v6.8.3 +ARG QT_COMMIT=bab1fecd556ea561c4a89686293116741acfa1b4 +ARG ANDROID_NDK_REVISION=26d +ARG ANDROID_NDK_HASH=eefeafe7ccf177de7cc57158da585e7af119bb7504a63604ad719e4b2a328b54 +ARG ANDROID_SDK_REVISION=11076708_latest +ARG ANDROID_SDK_HASH=2d2d50857e4eb553af5a6dc3ad507a17adf43d115264b1afc116f95c92e5e258 +ARG QT_HOST_VERSION=6.8.3 +ARG QT_HOST_SHA256=8bbea76f5298021048f54c3e2934d7a73e2e6498ebc5a0cb30de16e24a66d847 WORKDIR /opt/android ENV WORKDIR=/opt/android -ENV ANDROID_NATIVE_API_LEVEL=31 +ENV ANDROID_NATIVE_API_LEVEL=34 ENV ANDROID_API=android-${ANDROID_NATIVE_API_LEVEL} ENV ANDROID_CLANG=aarch64-linux-android${ANDROID_NATIVE_API_LEVEL}-clang ENV ANDROID_CLANGPP=aarch64-linux-android${ANDROID_NATIVE_API_LEVEL}-clang++ ENV ANDROID_NDK_ROOT=${WORKDIR}/android-ndk-r${ANDROID_NDK_REVISION} ENV ANDROID_SDK_ROOT=${WORKDIR}/cmdline-tools -ENV JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64 -ENV PATH=${JAVA_HOME}/bin:${PATH} +ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 +ENV PATH=${WORKDIR}/qt-host/bin:${WORKDIR}/qt-host/libexec:${JAVA_HOME}/bin:${PATH} ENV PREFIX=${WORKDIR}/prefix +ENV QT_HOST_PREFIX=${WORKDIR}/qt-host +ENV LD_LIBRARY_PATH=${QT_HOST_PREFIX}/lib:${QT_HOST_PREFIX} ENV TOOLCHAIN_DIR=${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64 ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update \ - && apt-get install -y ant automake build-essential ca-certificates-java file gettext git libc6 libncurses5 \ - libssl-dev libstdc++6 libtinfo5 libtool libz1 ninja-build openjdk-11-jdk-headless openjdk-11-jre-headless pkg-config python3 \ - unzip wget + && apt-get install -y ant aria2 automake build-essential ca-certificates-java file gettext git libc6 libncurses5 \ + libdbus-1-3 libegl1 libfontconfig1 libfreetype6 libgl1 libglib2.0-0 libgssapi-krb5-2 libssl-dev \ + libstdc++6 libtinfo5 libtool libx11-6 libxkbcommon0 libz1 libzstd1 libarchive-tools ninja-build openjdk-17-jdk-headless \ + openjdk-17-jre-headless pkg-config python3 unzip wget \ + && rm -rf /var/lib/apt/lists/* RUN PACKAGE_NAME=commandlinetools-linux-${ANDROID_SDK_REVISION}.zip \ && wget -q https://dl.google.com/android/repository/${PACKAGE_NAME} \ @@ -35,15 +42,42 @@ RUN PACKAGE_NAME=commandlinetools-linux-${ANDROID_SDK_REVISION}.zip \ RUN PACKAGE_NAME=android-ndk-r${ANDROID_NDK_REVISION}-linux.zip \ && wget -q https://dl.google.com/android/repository/${PACKAGE_NAME} \ - && echo "${ANDROID_NDK_HASH} ${PACKAGE_NAME}" | sha1sum -c \ + && echo "${ANDROID_NDK_HASH} ${PACKAGE_NAME}" | sha256sum -c \ && unzip -q ${PACKAGE_NAME} \ && rm -f ${PACKAGE_NAME} -RUN echo y | ${ANDROID_SDK_ROOT}/bin/sdkmanager --sdk_root=${ANDROID_SDK_ROOT} "build-tools;28.0.3" "platforms;${ANDROID_API}" "tools" > /dev/null +RUN yes | ${ANDROID_SDK_ROOT}/bin/sdkmanager --sdk_root=${ANDROID_SDK_ROOT} \ + "build-tools;35.0.1" "platforms;${ANDROID_API}" > /dev/null + +# Qt 6 requires CMake 3.21 or newer. Keep the version in sync with the other +# reproducible Docker builds. +RUN git clone -b v4.3.3 --depth 1 https://github.com/Kitware/CMake \ + && cd CMake \ + && git reset --hard 06cc1d04d8d5a4e13abdadfe20f6937787b1968e \ + && ./bootstrap --parallel=${THREADS} -- -DCMAKE_USE_OPENSSL=OFF \ + && make -j${THREADS} \ + && make install \ + && cd .. \ + && rm -rf CMake ENV HOST_PATH=${PATH} ENV PATH=${TOOLCHAIN_DIR}/aarch64-linux-android/bin:${TOOLCHAIN_DIR}/bin:${PATH} +COPY .github/qt_helper.py /tmp/qt_helper.py + +RUN mkdir -p "${QT_HOST_PREFIX}" \ + && cd "${QT_HOST_PREFIX}" \ + && python3 /tmp/qt_helper.py linux_x64 desktop "${QT_HOST_VERSION}" linux_gcc_64 "${QT_HOST_SHA256}" \ + --add-package addons.qtshadertools \ + --archive 'qtbase-*.7z' \ + --archive 'qtdeclarative-*.7z' \ + --archive 'qttools-*.7z' \ + --archive 'icu-*.7z' \ + --archive 'qtshadertools-*.7z' \ + && rm /tmp/qt_helper.py \ + && printf '%s\n%s\n' "${QT_HOST_PREFIX}/lib" "${QT_HOST_PREFIX}" > /etc/ld.so.conf.d/qt-host.conf \ + && ldconfig + ARG ZLIB_VERSION=1.3.1 ARG ZLIB_HASH=9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23 RUN wget -q https://github.com/madler/zlib/releases/download/v${ZLIB_VERSION}/zlib-${ZLIB_VERSION}.tar.gz \ @@ -56,41 +90,61 @@ RUN wget -q https://github.com/madler/zlib/releases/download/v${ZLIB_VERSION}/zl && make -j${THREADS} install \ && rm -rf $(pwd) -RUN git clone https://code.qt.io/qt/qt5.git -b ${QT_VERSION} --depth 1 \ - && cd qt5 \ - && git reset --hard dc2ac680fa9d0ef7b0d9520859593d13951bedea \ - && perl init-repository --module-subset=default,-qtwebengine \ - && PATH=${HOST_PATH} ./configure -v -developer-build -release \ - -xplatform android-clang \ - -android-ndk-platform ${ANDROID_API} \ - -android-ndk ${ANDROID_NDK_ROOT} \ - -android-sdk ${ANDROID_SDK_ROOT} \ - -android-ndk-host linux-x86_64 \ - -no-dbus \ - -opengl es2 \ - -no-use-gold-linker \ - -no-sql-mysql \ - -opensource -confirm-license \ - -android-arch arm64-v8a \ - -prefix ${PREFIX} \ - -nomake tools -nomake tests -nomake examples \ - -skip qtwebengine \ - -skip qtserialport \ - -skip qtconnectivity \ - -skip qttranslations \ - -skip qtpurchasing \ - -skip qtgamepad -skip qtscript -skip qtdoc \ - -no-warnings-are-errors \ - && PATH=${HOST_PATH} make -j${THREADS} \ - && PATH=${HOST_PATH} make -j${THREADS} install \ - && cd qttools/src/linguist/lrelease \ - && ../../../../qtbase/bin/qmake \ - && PATH=${HOST_PATH} make -j${THREADS} install \ - && cd ../../../.. \ - && rm -rf $(pwd) +RUN git clone https://github.com/qt/qt5.git -b ${QT_VERSION} --depth 1 qt-sources \ + && git -C qt-sources reset --hard ${QT_COMMIT} \ + && mkdir qt-build-android \ + && cd qt-build-android \ + && PATH=${HOST_PATH} ../qt-sources/configure -init-submodules \ + -submodules qtbase,qtdeclarative,qtshadertools,qtsvg,qttools \ + -prefix ${PREFIX} \ + -qt-host-path ${QT_HOST_PREFIX} \ + -android-ndk ${ANDROID_NDK_ROOT} \ + -android-sdk ${ANDROID_SDK_ROOT} \ + -android-abis arm64-v8a \ + -android-ndk-platform ${ANDROID_API} \ + -opensource -confirm-license -release \ + -opengl es2 -no-dbus -no-openssl -no-sql-mysql -no-sql-sqlite \ + -skip qtactiveqt \ + -skip qtlanguageserver \ + -skip qtquicktimeline \ + -no-feature-http \ + -no-feature-ssl \ + -no-feature-dtls \ + -no-feature-ocsp \ + -no-feature-networkproxy \ + -no-feature-socks5 \ + -no-feature-networkdiskcache \ + -no-feature-brotli \ + -no-feature-dnslookup \ + -no-feature-topleveldomain \ + -no-feature-udpsocket \ + -no-feature-system-proxies \ + -no-feature-sctp \ + -no-feature-qml-worker-script \ + -no-feature-printsupport \ + -no-feature-pdf \ + -no-feature-vulkan \ + -no-feature-sessionmanager \ + -no-feature-assistant \ + -no-feature-designer \ + -no-feature-qdoc \ + -no-feature-clang \ + -no-feature-clangcpp \ + -no-feature-distancefieldgenerator \ + -no-feature-pixeltool \ + -no-feature-qdbus \ + -no-feature-qev \ + -no-feature-qtattributionsscanner \ + -no-feature-qtdiag \ + -no-feature-qtplugininfo \ + -nomake examples -nomake tests \ + && PATH=${HOST_PATH} cmake --build . --parallel ${THREADS} \ + && PATH=${HOST_PATH} cmake --install . \ + && cd .. \ + && rm -rf qt-build-android qt-sources -ARG ICONV_VERSION=1.16 -ARG ICONV_HASH=e6a1b1b589654277ee790cce3734f07876ac4ccfaecbee8afa0b649cf529cc04 +ARG ICONV_VERSION=1.18 +ARG ICONV_HASH=3b08f5f4f9b4eb82f151a7040bfd6fe6c6fb922efe4b1659c66ea933276965e8 RUN wget -q https://ftp.gnu.org/pub/gnu/libiconv/libiconv-${ICONV_VERSION}.tar.gz \ && echo "${ICONV_HASH} libiconv-${ICONV_VERSION}.tar.gz" | sha256sum -c \ && tar -xzf libiconv-${ICONV_VERSION}.tar.gz \ @@ -198,42 +252,38 @@ RUN git clone -b libgcrypt-1.10.1 --depth 1 https://github.com/gpg/libgcrypt \ && make -j${THREADS} install \ && rm -rf $(pwd) -RUN git clone -b v3.31.4 --depth 1 https://github.com/Kitware/CMake \ - && cd CMake \ - && git reset --hard 569b821a138a4d3f7f4cc42c0cf5ae5e68d56f96 \ - && PATH=${HOST_PATH} ./bootstrap \ - && PATH=${HOST_PATH} make -j${THREADS} \ - && PATH=${HOST_PATH} make -j${THREADS} install \ - && rm -rf $(pwd) - -# Workaround +# Keep only the SDK components required by androiddeployqt. Moving these out +# of cmdline-tools also avoids sdkmanager treating its own directory as the +# SDK root during deployment. ENV NEW_SDK_ROOT=${WORKDIR}/sdk RUN mkdir ${NEW_SDK_ROOT} \ && cp -r ${ANDROID_SDK_ROOT}/licenses ${NEW_SDK_ROOT} \ && cp -r ${ANDROID_SDK_ROOT}/platforms ${NEW_SDK_ROOT} \ - && cp -r ${ANDROID_SDK_ROOT}/build-tools ${NEW_SDK_ROOT} + && cp -r ${ANDROID_SDK_ROOT}/build-tools ${NEW_SDK_ROOT} \ + && rm -rf ${ANDROID_SDK_ROOT} \ + && mv ${NEW_SDK_ROOT} ${ANDROID_SDK_ROOT} CMD set -ex \ + && git config --global --add safe.directory '*' \ && cd /monero-gui \ && mkdir -p build/Android/release \ && cd build/Android/release \ - && cmake -G Ninja \ - -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake" \ + && cmake \ + -G Ninja \ + -DCMAKE_TOOLCHAIN_FILE="${PREFIX}/lib/cmake/Qt6/qt.toolchain.cmake" \ -DCMAKE_PREFIX_PATH="${PREFIX}" \ -DCMAKE_FIND_ROOT_PATH="${PREFIX}" \ -DCMAKE_BUILD_TYPE=Release \ -DARCH="armv8-a" \ -DANDROID_NATIVE_API_LEVEL=${ANDROID_NATIVE_API_LEVEL} \ + -DANDROID_PLATFORM=${ANDROID_API} \ -DANDROID_ABI="arm64-v8a" \ -DANDROID_TOOLCHAIN=clang \ -DBoost_USE_STATIC_RUNTIME=ON \ - -DLRELEASE_PATH="${PREFIX}/bin" \ - -DQT_ANDROID_APPLICATION_BINARY="monero-wallet-gui" \ - -DANDROID_SDK="${NEW_SDK_ROOT}" \ - -DWITH_SCANNER=ON \ + -DANDROID_SDK="${ANDROID_SDK_ROOT}" \ + -DWITH_SCANNER=OFF \ -DWITH_DESKTOP_ENTRY=OFF \ ../../.. \ - && PATH=${HOST_PATH} cmake --build . --target generate_translations_header \ && sed -i -e "/^build .*monero-wallet-gui.*:.*LINKER/,/^$/ { s#monero/external/randomx/librandomx.a##; s#-lm#-lm monero/external/randomx/librandomx.a#; }" build.ninja \ && cmake --build . --target monero-wallet-gui \ && cmake --build . --target apk diff --git a/Dockerfile.linux b/Dockerfile.linux index 0f82d4bec9..baa54ddc03 100644 --- a/Dockerfile.linux +++ b/Dockerfile.linux @@ -1,17 +1,47 @@ -FROM ubuntu:18.04 +FROM ubuntu:20.04 ARG THREADS=1 -ARG QT_VERSION=v5.15.19-lts-lgpl +ARG QT_VERSION=v6.8.3 +ARG QT_COMMIT=bab1fecd556ea561c4a89686293116741acfa1b4 ENV CFLAGS="-fPIC" ENV CPPFLAGS="-fPIC" ENV CXXFLAGS="-fPIC" ENV SOURCE_DATE_EPOCH=1397818193 +ENV QT_PREFIX=/depends/x86_64-linux-gnu + +ENV PATH="${QT_PREFIX}/bin:${PATH}" +ENV CMAKE_PREFIX_PATH="${QT_PREFIX}" + RUN apt update && \ - apt install -y automake autopoint bison gettext git gperf libgl1-mesa-dev libglib2.0-dev ninja-build \ - libpng-dev libpthread-stubs0-dev libsodium-dev libtool-bin libudev-dev libusb-1.0-0-dev mesa-common-dev \ - pkg-config python wget xutils-dev + DEBIAN_FRONTEND=noninteractive apt install -y \ + aria2 automake autopoint bison bzip2 ca-certificates curl gettext git g++ make \ + gperf libarchive-tools libgl1-mesa-dev libglib2.0-dev libpng-dev libpthread-stubs0-dev \ + libtool-bin libudev-dev mesa-common-dev \ + ninja-build pkg-config python3 python-is-python3 wget xutils-dev && \ + rm -rf /var/lib/apt/lists/* + +RUN git clone -b v0.18.5.1 --depth 1 https://github.com/monero-project/monero && \ + cd monero && \ + git reset --hard 4f92268d7c16741cfb41e5bbe2aa46cc260a9ea5 && \ + cp -a contrib/depends / && \ + cd .. && \ + rm -rf monero + +RUN make -j"$THREADS" -C /depends HOST=x86_64-linux-gnu + +# Qt 6 static builds require CMake 3.21 or newer. Debian 11 deliberately keeps +# the runtime glibc baseline old, so install a current CMake without changing +# the base distribution. +RUN git clone -b v4.3.3 --depth 1 https://github.com/Kitware/CMake && \ + cd CMake && \ + git reset --hard 06cc1d04d8d5a4e13abdadfe20f6937787b1968e && \ + ./bootstrap --parallel=$THREADS -- -DCMAKE_USE_OPENSSL=OFF && \ + make -j$THREADS && \ + make -j$THREADS install && \ + cd .. && \ + rm -rf CMake RUN git clone -b xorgproto-2020.1 --depth 1 https://gitlab.freedesktop.org/xorg/proto/xorgproto && \ cd xorgproto && \ @@ -21,9 +51,9 @@ RUN git clone -b xorgproto-2020.1 --depth 1 https://gitlab.freedesktop.org/xorg/ make -j$THREADS install && \ rm -rf $(pwd) -RUN git clone -b 1.12 --depth 1 https://gitlab.freedesktop.org/xorg/proto/xcbproto && \ +RUN git clone -b xcb-proto-1.17.0 --depth 1 https://gitlab.freedesktop.org/xorg/proto/xcbproto && \ cd xcbproto && \ - git reset --hard 6398e42131eedddde0d98759067dde933191f049 && \ + git reset --hard 77d7fc04da729ddc5ed4aacf30253726fac24dca && \ ./autogen.sh && \ make -j$THREADS && \ make -j$THREADS install && \ @@ -37,9 +67,9 @@ RUN git clone -b libXau-1.0.9 --depth 1 https://gitlab.freedesktop.org/xorg/lib/ make -j$THREADS install && \ rm -rf $(pwd) -RUN git clone -b 1.12 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb && \ +RUN git clone -b libxcb-1.17.0 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb && \ cd libxcb && \ - git reset --hard d34785a34f28fa6a00f8ce00d87e3132ff0f6467 && \ + git reset --hard 622152ee42a310876f10602601206954b8d0613e && \ ./autogen.sh --enable-shared --disable-static && \ make -j$THREADS && \ make -j$THREADS install && \ @@ -156,96 +186,56 @@ RUN git clone -b release-64-2 --depth 1 https://github.com/unicode-org/icu && \ make -j$THREADS install && \ rm -rf $(pwd) -RUN wget https://archives.boost.io/release/1.80.0/source/boost_1_80_0.tar.gz && \ - echo "4b2136f98bdd1f5857f1c3dea9ac2018effe65286cf251534b6ae20cc45e1847 boost_1_80_0.tar.gz" | sha256sum -c && \ - tar -xzf boost_1_80_0.tar.gz && \ - rm boost_1_80_0.tar.gz && \ - cd boost_1_80_0 && \ - ./bootstrap.sh && \ - ./b2 --with-atomic --with-system --with-filesystem --with-thread --with-date_time --with-chrono --with-regex --with-serialization --with-program_options --with-locale variant=release link=static runtime-link=static cflags="${CFLAGS}" cxxflags="${CXXFLAGS}" install -a --prefix=/usr && \ - rm -rf $(pwd) - -RUN wget https://www.openssl.org/source/openssl-1.1.1u.tar.gz && \ - echo "e2f8d84b523eecd06c7be7626830370300fbcc15386bf5142d72758f6963ebc6 openssl-1.1.1u.tar.gz" | sha256sum -c && \ - tar -xzf openssl-1.1.1u.tar.gz && \ - rm openssl-1.1.1u.tar.gz && \ - cd openssl-1.1.1u && \ - ./config no-shared no-zlib-dynamic --prefix=/usr --openssldir=/usr && \ +RUN git clone -b xcb-util-cursor-0.1.4 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-cursor && \ + cd libxcb-cursor && \ + git reset --hard 3d7e713e85af18d7e52cafdc9d20a2715048dee7 && \ + git submodule init && \ + git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \ + git -C m4 reset --hard c617eee22ae5c285e79e81ec39ce96862fd3262f && \ + ./autogen.sh --enable-shared --disable-static && \ make -j$THREADS && \ make -j$THREADS install && \ rm -rf $(pwd) -RUN wget https://www.nlnetlabs.nl/downloads/unbound/unbound-1.16.2.tar.gz && \ - echo "2e32f283820c24c51ca1dd8afecfdb747c7385a137abe865c99db4b257403581 unbound-1.16.2.tar.gz" | sha256sum -c && \ - tar -xzf unbound-1.16.2.tar.gz && \ - rm unbound-1.16.2.tar.gz && \ - cd unbound-1.16.2 && \ - ./configure --disable-shared --enable-static --without-pyunbound --with-libexpat=/usr --with-ssl=/usr --with-libevent=no --without-pythonmodule --disable-flto --with-pthreads --with-libunbound-only --with-pic && \ - make -j$THREADS && \ - make -j$THREADS install && \ - rm -rf $(pwd) +# CMake's FindFontconfig target does not expose the private dependencies of a +# static Fontconfig build. Bundle them so Qt tools and downstream static links +# do not depend on library ordering or pkg-config-specific metadata. +RUN printf 'create /tmp/libfontconfig.a\naddlib /usr/local/lib/libfontconfig.a\naddlib /usr/local/lib/libfreetype.a\naddlib /usr/lib/libexpat.a\nsave\nend\n' | ar -M && \ + mv /tmp/libfontconfig.a /usr/local/lib/libfontconfig.a && \ + ldconfig -RUN rm /usr/lib/x86_64-linux-gnu/libX11.a && \ - rm /usr/lib/x86_64-linux-gnu/libXext.a && \ - rm /usr/lib/x86_64-linux-gnu/libX11-xcb.a && \ - git clone https://code.qt.io/qt/qt5.git -b ${QT_VERSION} --depth 1 && \ - cd qt5 && \ - git reset --hard dc2ac680fa9d0ef7b0d9520859593d13951bedea && \ - git submodule update --init --depth 1 qtbase qtdeclarative qtgraphicaleffects qtimageformats qtmultimedia qtquickcontrols qtquickcontrols2 qtsvg qttools qttranslations qtx11extras && \ +RUN rm -f /usr/lib/x86_64-linux-gnu/libX11.a \ + /usr/lib/x86_64-linux-gnu/libXext.a \ + /usr/lib/x86_64-linux-gnu/libX11-xcb.a && \ + git clone https://github.com/qt/qt5.git -b ${QT_VERSION} --depth 1 qt-sources && \ + git -C qt-sources reset --hard ${QT_COMMIT} && \ sed -ri s/\(Libs:.*\)/\\1\ -lexpat/ /usr/local/lib/pkgconfig/fontconfig.pc && \ sed -ri s/\(Libs:.*\)/\\1\ -lz/ /usr/local/lib/pkgconfig/freetype2.pc && \ sed -ri s/\(Libs:.*\)/\\1\ -lXau/ /usr/local/lib/pkgconfig/xcb.pc && \ - sed -i s/\\/usr\\/X11R6\\/lib64/\\/usr\\/local\\/lib/ qtbase/mkspecs/linux-g++-64/qmake.conf && \ - ./configure --prefix=/usr -platform linux-g++-64 -opensource -confirm-license -release -static -no-avx \ - -opengl desktop -qpa xcb -xcb -xcb-xlib -feature-xlib -system-freetype -fontconfig -glib \ - -no-dbus -no-feature-qml-worker-script -no-linuxfb -no-openssl -no-sql-sqlite -no-kms -no-use-gold-linker \ - -qt-harfbuzz -qt-libjpeg -qt-libpng -qt-pcre -qt-zlib \ - -skip qt3d -skip qtandroidextras -skip qtcanvas3d -skip qtcharts -skip qtconnectivity -skip qtdatavis3d \ - -skip qtdoc -skip qtgamepad -skip qtlocation -skip qtmacextras -skip qtnetworkauth -skip qtpurchasing \ - -skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qttools \ - -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview \ - -skip qtwinextras -skip qtx11extras -skip gamepad -skip serialbus -skip location -skip webengine \ - -nomake examples -nomake tests -nomake tools && \ - make -j$THREADS && \ - make -j$THREADS install && \ - cd qttools/src/linguist/lrelease && \ - ../../../../qtbase/bin/qmake && \ - make -j$THREADS && \ - make -j$THREADS install && \ - cd ../../../.. && \ - rm -rf $(pwd) - -RUN git clone -b v1.0.26 --depth 1 https://github.com/libusb/libusb && \ - cd libusb && \ - git reset --hard 4239bc3a50014b8e6a5a2a59df1fff3b7469543b && \ - ./autogen.sh --disable-shared --enable-static && \ - make -j$THREADS && \ - make -j$THREADS install && \ - rm -rf $(pwd) - -RUN git clone -b hidapi-0.15.0 --depth 1 https://github.com/libusb/hidapi && \ - cd hidapi && \ - git reset --hard d6b2a974608dec3b76fb1e36c189f22b9cf3650c && \ - ./bootstrap && \ - ./configure --disable-shared --enable-static && \ - make -j$THREADS && \ - make -j$THREADS install && \ - rm -rf $(pwd) - -RUN git clone -b v4.3.4 --depth 1 https://github.com/zeromq/libzmq && \ - cd libzmq && \ - git reset --hard 4097855ddaaa65ed7b5e8cb86d143842a594eebd && \ - ./autogen.sh && \ - ./configure --disable-shared --enable-static --disable-libunwind --with-libsodium && \ - make -j$THREADS && \ - make -j$THREADS install && \ - rm -rf $(pwd) + mkdir qt-build && \ + cd qt-build && \ + ../qt-sources/configure -init-submodules \ + -submodules qtbase,qtdeclarative,qtshadertools,qtsvg,qttools \ + -prefix ${QT_PREFIX} -opensource -confirm-license -release -static -no-avx \ + -opengl desktop -xcb -no-feature-xlib -system-freetype -fontconfig -no-feature-eglfs -no-glib \ + -no-dbus -no-feature-qml-worker-script -no-feature-linuxfb -no-openssl -no-sql-sqlite -no-feature-kms \ + -no-feature-printsupport -no-feature-pdf -no-feature-vulkan -no-feature-sessionmanager -no-feature-gtk3 \ + -no-feature-assistant -no-feature-clang -no-feature-clangcpp -no-feature-qdoc -no-feature-designer -no-feature-distancefieldgenerator \ + -no-feature-pixeltool -no-feature-qdbus -no-feature-qtattributionsscanner -no-feature-qtdiag -no-feature-qtplugininfo \ + -no-feature-http -no-feature-ssl -no-feature-dtls -no-feature-ocsp -no-feature-networkproxy -no-feature-socks5 -no-feature-networkdiskcache \ + -no-feature-brotli -no-feature-dnslookup -no-feature-topleveldomain -no-feature-udpsocket -no-feature-system-proxies \ + -no-feature-sctp -skip qtactiveqt -skip qtlanguageserver -skip qtquicktimeline -qt-harfbuzz -qt-libjpeg -qt-libpng -qt-pcre -qt-zlib \ + -nomake examples -nomake tests && \ + cmake --build . --parallel $THREADS && \ + cmake --install . && \ + cd .. && \ + rm -rf qt-build qt-sources RUN git clone -b libgpg-error-1.45 --depth 1 https://github.com/gpg/libgpg-error && \ cd libgpg-error && \ git reset --hard dbac537e5e865fb6f3aa8596d213aa8c47a9dea1 && \ ./autogen.sh && \ - ./configure --disable-shared --enable-static --disable-doc --disable-tests && \ + ./configure --disable-shared --enable-static --disable-doc --disable-tests --prefix="${QT_PREFIX}" && \ make -j$THREADS && \ make -j$THREADS install && \ rm -rf $(pwd) @@ -254,24 +244,7 @@ RUN git clone -b libgcrypt-1.10.1 --depth 1 https://github.com/gpg/libgcrypt && cd libgcrypt && \ git reset --hard ae0e567820c37f9640440b3cff77d7c185aa6742 && \ ./autogen.sh && \ - ./configure --disable-shared --enable-static --disable-doc && \ - make -j$THREADS && \ - make -j$THREADS install && \ - rm -rf $(pwd) - -RUN git clone -b v21.5 --depth 1 https://github.com/protocolbuffers/protobuf && \ - cd protobuf && \ - git reset --hard ab840345966d0fa8e7100d771c92a73bfbadd25c && \ - ./autogen.sh && \ - ./configure --enable-static --disable-shared && \ - make -j$THREADS && \ - make -j$THREADS install && \ - rm -rf $(pwd) - -RUN git clone -b v3.24.0 --depth 1 https://github.com/Kitware/CMake && \ - cd CMake && \ - git reset --hard 4be24f031a4829db75b85062cc67125035d8831e && \ - ./bootstrap && \ + ./configure --disable-shared --enable-static --disable-doc --with-gpg-error-prefix="${QT_PREFIX}" --prefix="${QT_PREFIX}" && \ make -j$THREADS && \ make -j$THREADS install && \ rm -rf $(pwd) diff --git a/Dockerfile.windows b/Dockerfile.windows index 3fde387439..7b9c9a58bb 100644 --- a/Dockerfile.windows +++ b/Dockerfile.windows @@ -1,16 +1,50 @@ -FROM ubuntu:20.04 +FROM ubuntu:24.04 ARG THREADS=1 -ARG QT_VERSION=v5.15.19-lts-lgpl +ARG QT_VERSION=6.8.3 +ARG QT_COMMIT=bab1fecd556ea561c4a89686293116741acfa1b4 +ARG QT_HOST_SHA256=8bbea76f5298021048f54c3e2934d7a73e2e6498ebc5a0cb30de16e24a66d847 + ENV SOURCE_DATE_EPOCH=1397818193 +ENV QT_HOST_PATH=/opt/qt-host +ENV LD_LIBRARY_PATH=${QT_HOST_PATH}/lib:${QT_HOST_PATH} +ENV QT_TARGET_PREFIX=/depends/x86_64-w64-mingw32 +ENV PATH=/opt/qt-host/bin:/opt/qt-host/libexec:${PATH} RUN apt update && \ - DEBIAN_FRONTEND=noninteractive apt install -y build-essential cmake g++-mingw-w64 gettext git libtool ninja-build pkg-config \ - python && \ + DEBIAN_FRONTEND=noninteractive apt install -y \ + autoconf \ + automake \ + aria2 \ + autopoint \ + bison \ + build-essential \ + ca-certificates \ + cmake \ + curl \ + flex \ + g++-mingw-w64 \ + gettext \ + git \ + gperf \ + libarchive-tools \ + libdbus-1-3 \ + libfontconfig1-dev \ + libglib2.0-0 \ + libgl1-mesa-dev \ + libgssapi-krb5-2 \ + libtool \ + libxkbcommon0 \ + ninja-build \ + perl \ + pkg-config \ + python3 \ + python-is-python3 \ + xz-utils && \ rm -rf /var/lib/apt/lists/* -RUN update-alternatives --set x86_64-w64-mingw32-g++ $(which x86_64-w64-mingw32-g++-posix) && \ - update-alternatives --set x86_64-w64-mingw32-gcc $(which x86_64-w64-mingw32-gcc-posix) +RUN update-alternatives --set x86_64-w64-mingw32-g++ "$(which x86_64-w64-mingw32-g++-posix)" && \ + update-alternatives --set x86_64-w64-mingw32-gcc "$(which x86_64-w64-mingw32-gcc-posix)" RUN git clone -b v0.18.5.1 --depth 1 https://github.com/monero-project/monero && \ cd monero && \ @@ -19,44 +53,108 @@ RUN git clone -b v0.18.5.1 --depth 1 https://github.com/monero-project/monero && cd .. && \ rm -rf monero -RUN make -j$THREADS -C /depends HOST=x86_64-w64-mingw32 NO_QT=1 +RUN make -j"$THREADS" -C /depends HOST=x86_64-w64-mingw32 + +COPY .github/qt_helper.py /tmp/qt_helper.py + +# Reuse Qt's matching Linux host tools instead of compiling them from source. +RUN mkdir -p "${QT_HOST_PATH}" \ + && cd "${QT_HOST_PATH}" \ + && python3 /tmp/qt_helper.py linux_x64 desktop "${QT_VERSION}" linux_gcc_64 "${QT_HOST_SHA256}" \ + --add-package addons.qtshadertools \ + --archive 'qtbase-*.7z' \ + --archive 'qtdeclarative-*.7z' \ + --archive 'qttools-*.7z' \ + --archive 'icu-*.7z' \ + --archive 'qtshadertools-*.7z' \ + && rm /tmp/qt_helper.py \ + && printf '%s\n%s\n' "${QT_HOST_PATH}/lib" "${QT_HOST_PATH}" > /etc/ld.so.conf.d/qt-host.conf \ + && ldconfig -RUN git clone https://code.qt.io/qt/qt5.git -b ${QT_VERSION} --depth 1 && \ - cd qt5 && \ - git reset --hard dc2ac680fa9d0ef7b0d9520859593d13951bedea && \ - git submodule update --init --depth 1 qtbase qtdeclarative qtgraphicaleffects qtimageformats qtmultimedia qtquickcontrols qtquickcontrols2 qtsvg qttools qttranslations && \ - ./configure --prefix=/depends/x86_64-w64-mingw32 -xplatform win32-g++ \ +RUN git clone https://github.com/qt/qt5.git -b "v${QT_VERSION}" --depth 1 qt-sources && \ + git -C qt-sources reset --hard "${QT_COMMIT}" && \ + mkdir /qt-target-build && \ + cd /qt-target-build && \ + /qt-sources/configure -init-submodules \ + -submodules qtbase,qtdeclarative,qtshadertools,qtsvg,qttools \ + -skip qtactiveqt \ + -skip qtlanguageserver \ + -skip qtquicktimeline \ + -prefix "${QT_TARGET_PREFIX}" \ + -qt-host-path "${QT_HOST_PATH}" \ + -xplatform win32-g++ \ -device-option CROSS_COMPILE=/usr/bin/x86_64-w64-mingw32- \ - -I $(pwd)/qtbase/src/3rdparty/angle/include \ - -opensource -confirm-license -release -static -static-runtime -opengl dynamic -no-angle \ - -no-avx -no-openssl -no-sql-sqlite \ - -no-feature-qml-worker-script -no-openssl -no-sql-sqlite \ - -qt-freetype -qt-harfbuzz -qt-libjpeg -qt-libpng -qt-pcre -qt-zlib \ - -skip gamepad -skip location -skip qt3d -skip qtactiveqt -skip qtandroidextras \ - -skip qtcanvas3d -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdoc \ - -skip qtgamepad -skip qtlocation -skip qtmacextras -skip qtnetworkauth -skip qtpurchasing \ - -skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport \ - -skip qtspeech -skip qttools -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel \ - -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtwinextras -skip qtx11extras \ - -skip serialbus -skip webengine \ - -nomake examples -nomake tests -nomake tools && \ - make -j$THREADS && \ - make -j$THREADS install && \ - cd qttools/src/linguist/lrelease && \ - ../../../../qtbase/bin/qmake && \ - make -j$THREADS && \ - make -j$THREADS install && \ - cd ../../../.. && \ - rm -rf $(pwd) + -opensource \ + -confirm-license \ + -release \ + -static \ + -static-runtime \ + -opengl dynamic \ + -no-intelcet \ + -no-stack-protector \ + -no-stack-clash-protection \ + -no-avx \ + -no-openssl \ + -no-sql-sqlite \ + -no-feature-http \ + -no-feature-ssl \ + -no-feature-dtls \ + -no-feature-ocsp \ + -no-feature-networkproxy \ + -no-feature-socks5 \ + -no-feature-networkdiskcache \ + -no-feature-brotli \ + -no-feature-dnslookup \ + -no-feature-topleveldomain \ + -no-feature-udpsocket \ + -no-feature-system-proxies \ + -no-feature-sctp \ + -no-feature-schannel \ + -no-feature-qml-worker-script \ + -no-feature-printsupport \ + -no-feature-pdf \ + -no-feature-vulkan \ + -no-feature-sessionmanager \ + -no-feature-assistant \ + -no-feature-designer \ + -no-feature-qdoc \ + -no-feature-clang \ + -no-feature-clangcpp \ + -no-feature-distancefieldgenerator \ + -no-feature-pixeltool \ + -no-feature-qdbus \ + -no-feature-qev \ + -no-feature-qtattributionsscanner \ + -no-feature-qtdiag \ + -no-feature-qtplugininfo \ + -qt-freetype \ + -qt-harfbuzz \ + -qt-libjpeg \ + -qt-libpng \ + -qt-pcre \ + -qt-zlib \ + -nomake examples \ + -nomake tests \ + -- \ + -DCMAKE_TOOLCHAIN_FILE=/depends/x86_64-w64-mingw32/share/toolchain.cmake && \ + cmake --build . --parallel "$THREADS" && \ + cmake --install . && \ + cd / && \ + rm -rf qt-target-build qt-sources RUN git clone -b libgpg-error-1.38 --depth 1 https://github.com/gpg/libgpg-error && \ cd libgpg-error && \ git reset --hard 71d278824c5fe61865f7927a2ed1aa3115f9e439 && \ ./autogen.sh && \ - ./configure --disable-shared --enable-static --disable-doc --disable-tests \ - --host=x86_64-w64-mingw32 --prefix=/depends/x86_64-w64-mingw32 && \ - make -j$THREADS && \ - make -j$THREADS install && \ + ./configure \ + --disable-shared \ + --enable-static \ + --disable-doc \ + --disable-tests \ + --host=x86_64-w64-mingw32 \ + --prefix=/depends/x86_64-w64-mingw32 && \ + make -j"$THREADS" && \ + make -j"$THREADS" install && \ cd .. && \ rm -rf libgpg-error @@ -64,10 +162,14 @@ RUN git clone -b libgcrypt-1.8.5 --depth 1 https://github.com/gpg/libgcrypt && \ cd libgcrypt && \ git reset --hard 56606331bc2a80536db9fc11ad53695126007298 && \ ./autogen.sh && \ - ./configure --disable-shared --enable-static --disable-doc \ - --host=x86_64-w64-mingw32 --prefix=/depends/x86_64-w64-mingw32 \ + ./configure \ + --disable-shared \ + --enable-static \ + --disable-doc \ + --host=x86_64-w64-mingw32 \ + --prefix=/depends/x86_64-w64-mingw32 \ --with-gpg-error-prefix=/depends/x86_64-w64-mingw32 && \ - make -j$THREADS && \ - make -j$THREADS install && \ + make -j"$THREADS" && \ + make -j"$THREADS" install && \ cd .. && \ rm -rf libgcrypt diff --git a/LeftPanel.qml b/LeftPanel.qml index 4365f996a7..cf624da8e9 100644 --- a/LeftPanel.qml +++ b/LeftPanel.qml @@ -26,13 +26,11 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.1 -import QtGraphicalEffects 1.0 +import QtQuick +import QtQuick.Effects import moneroComponents.Wallet 1.0 import moneroComponents.NetworkType 1.0 import moneroComponents.Clipboard 1.0 -import FontAwesome 1.0 import "components" as MoneroComponents import "components/effects/" as MoneroEffects @@ -106,6 +104,9 @@ Rectangle { anchors.topMargin: (persistentSettings.customDecorations)? 50 : 0 Item { + width: parent.width + height: parent.height + Item { anchors.left: parent.left anchors.top: parent.top @@ -116,23 +117,23 @@ Rectangle { Image { id: card - visible: !isOpenGL || MoneroComponents.Style.blackTheme + visible: GraphicsInfo.api === GraphicsInfo.Software || MoneroComponents.Style.blackTheme width: 260 height: 135 fillMode: Image.PreserveAspectFit source: MoneroComponents.Style.blackTheme ? "qrc:///images/card-background-black" + (currentAccountIndex % MoneroComponents.Style.accountColors.length) + ".png" : "qrc:///images/card-background-white.png" } - DropShadow { - visible: isOpenGL && !MoneroComponents.Style.blackTheme + MultiEffect { + visible: GraphicsInfo.api !== GraphicsInfo.Software && !MoneroComponents.Style.blackTheme anchors.fill: card - horizontalOffset: 3 - verticalOffset: 3 - radius: 10.0 - samples: 15 - color: "#3B000000" source: card - cached: true + shadowEnabled: true + shadowHorizontalOffset: 3 + shadowVerticalOffset: 3 + shadowBlur: 0.625 + blurMax: 16 + shadowColor: "#3B000000" } MoneroComponents.TextPlain { @@ -308,7 +309,7 @@ Rectangle { hoverEnabled: true anchors.fill: parent cursorShape: Qt.PointingHandCursor - onClicked: balancePart1MouseArea.clicked(mouse) + onClicked: (mouse) => balancePart1MouseArea.clicked(mouse) } } diff --git a/MiddlePanel.qml b/MiddlePanel.qml index cd703a165a..db58a7fc25 100644 --- a/MiddlePanel.qml +++ b/MiddlePanel.qml @@ -27,12 +27,10 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQml 2.0 -import QtQuick 2.9 -import QtQuick.Controls 2.0 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.1 -import QtGraphicalEffects 1.0 +import QtQml +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts import moneroComponents.Wallet 1.0 import "./pages" @@ -98,8 +96,13 @@ Rectangle { } previousView = currentView if (currentView) { - stackView.replace(currentView) - // Component.onCompleted is called before wallet is initilized + if (stackView.currentItem !== currentView) { + if (stackView.depth > 0) { + stackView.replace(currentView) + } else { + stackView.push(currentView) + } + } if (typeof currentView.onPageCompleted === "function") { currentView.onPageCompleted(); } @@ -134,7 +137,7 @@ Rectangle { }, State { name: "Receive" PropertyChanges { target: root; currentView: receiveView } - PropertyChanges { target: mainFlickable; contentHeight: receiveView.receiveHeight + 80 } + PropertyChanges { target: mainFlickable; contentHeight: mainFlickable.height; interactive: false } }, State { name: "Merchant" PropertyChanges { target: root; currentView: merchantView } @@ -158,7 +161,7 @@ Rectangle { }, State { name: "Account" PropertyChanges { target: root; currentView: accountView } - PropertyChanges { target: mainFlickable; contentHeight: accountView.accountHeight + 80 } + PropertyChanges { target: mainFlickable; contentHeight: mainFlickable.height; interactive: false } } ] @@ -183,6 +186,7 @@ Rectangle { boundsBehavior: isMac ? Flickable.DragAndOvershootBounds : Flickable.StopAtBounds ScrollBar.vertical: ScrollBar { + visible: root.currentView !== root.receiveView && root.currentView !== root.accountView parent: root anchors.left: parent.right anchors.leftMargin: -14 // 10 margin + 4 scrollbar width @@ -204,26 +208,6 @@ Rectangle { anchors.fill:parent clip: true // otherwise animation will affect left panel - delegate: StackViewDelegate { - pushTransition: StackViewTransition { - PropertyAnimation { - target: enterItem - property: "x" - from: 0 - target.width - to: 0 - duration: 300 - easing.type: Easing.OutCubic - } - PropertyAnimation { - target: exitItem - property: "x" - from: 0 - to: target.width - duration: 300 - easing.type: Easing.OutCubic - } - } - } } }// flickable diff --git a/README.md b/README.md index 408135ab5b..2ec047011b 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ Packaging for your favorite distribution would be a welcome contribution! ## Compiling the Monero GUI from source -*Note*: Qt 5.12 is the minimum version required to build the GUI. +*Note*: Qt 6.8 is the minimum version required to build the GUI. *Note*: Official GUI releases use monero-wallet-gui from this process alongside the supporting binaries (monerod, etc) from the [CLI deterministic builds](https://github.com/monero-project/monero/blob/release-v0.18/contrib/gitian/README.md). @@ -138,17 +138,22 @@ Packaging for your favorite distribution would be a welcome contribution! ``` \* `4` - number of CPU threads to use + The image builds the non-Qt dependencies with Monero's `contrib/depends` + system and installs Qt into the resulting `/depends/x86_64-linux-gnu` + prefix. + 4. Build ``` - docker run --rm -it -v :/monero-gui -w /monero-gui monero:build-env-linux sh -c 'make release-static' + docker run --rm -it -v :/monero-gui -w /monero-gui monero:build-env-linux sh -c 'make depends root=/depends target=x86_64-linux-gnu tag=linux-x64' ``` \* `` - absolute path to `monero-gui` directory \* Set `CMAKE_BUILD_PARALLEL_LEVEL` to control the number of parallel build jobs, e.g. add `-e CMAKE_BUILD_PARALLEL_LEVEL=4` to the `docker run` command -5. Monero GUI Linux static binary will be placed in `monero-gui/build/release/bin` directory + +5. Monero GUI Linux static binary will be placed in `monero-gui/build/x86_64-linux-gnu/release/bin` directory 6. (*Note*) This process is only for building `monero-wallet-gui`, `monerod` has to be built separately according to the instructions in the `monero` repository. 7. (*Optional*) Compare `monero-wallet-gui` SHA-256 hash to the one obtained from a trusted source ``` - docker run --rm -it -v :/monero-gui -w /monero-gui monero:build-env-linux sh -c 'shasum -a 256 /monero-gui/build/release/bin/monero-wallet-gui' + docker run --rm -it -v :/monero-gui -w /monero-gui monero:build-env-linux sh -c 'shasum -a 256 /monero-gui/build/x86_64-linux-gnu/release/bin/monero-wallet-gui' ``` \* `` - absolute path to `monero-gui` directory @@ -203,8 +208,6 @@ Packaging for your favorite distribution would be a welcome contribution! ### Building on Linux -(Tested on Ubuntu 17.10 x64, Ubuntu 18.04 x64 and Gentoo x64) - 1. Install Monero dependencies - For Debian distributions (Debian, Ubuntu, Mint, Tails...) @@ -221,32 +224,32 @@ Packaging for your favorite distribution would be a welcome contribution! 2. Install Qt: - *Note*: The Qt 5.12 or newer requirement makes **some** distributions (mostly based on Debian, like Ubuntu 16.x or Linux Mint 18.x) obsolete due to their repositories containing an older Qt version. + *Note*: The Qt 6.8 or newer requirement makes **some** distributions obsolete due to their repositories containing an older Qt version. - The recommended way is to install 5.12 or newer from the [official Qt installer](https://www.qt.io/download-qt-installer) or [compiling it yourself](https://wiki.qt.io/Install_Qt_5_on_Ubuntu). This ensures you have the correct version. Higher versions *can* work but as it differs from our production build target, slight differences may occur. + The recommended way is to install 6.8 or newer from the [official Qt installer](https://www.qt.io/download-qt-installer) or [compiling it yourself](https://doc.qt.io/qt-6/build-sources.html). This ensures you have the correct version. Higher versions *can* work but as it differs from our production build target, slight differences may occur. The following instructions will fetch Qt from your distribution's repositories instead. Take note of what version it installs. Your mileage may vary. - For Debian distributions (Debian, Ubuntu, Mint, Tails...) - `sudo apt install qtbase5-dev qtdeclarative5-dev qml-module-qtqml-models2 qml-module-qtquick-controls qml-module-qtquick-controls2 qml-module-qtquick-dialogs qml-module-qtquick-xmllistmodel qml-module-qt-labs-settings qml-module-qt-labs-platform qml-module-qt-labs-folderlistmodel qttools5-dev-tools qml-module-qtquick-templates2 libqt5svg5-dev` + `sudo apt install qt6-base-dev qt6-declarative-dev qt6-svg-dev qt6-tools-dev qt6-tools-dev-tools qml6-module-qtcore qml6-module-qtqml qml6-module-qtqml-models qml6-module-qtquick qml6-module-qtquick-controls qml6-module-qtquick-dialogs qml6-module-qtquick-effects qml6-module-qtquick-layouts qml6-module-qtquick-shapes qml6-module-qtquick-window qml6-module-qt-labs-folderlistmodel qml6-module-qt-labs-platform` - For Gentoo The *qml* USE flag must be enabled. - `sudo emerge dev-qt/qtcore:5 dev-qt/qtdeclarative:5 dev-qt/qtquickcontrols:5 dev-qt/qtquickcontrols2:5 dev-qt/qtgraphicaleffects:5` + `sudo emerge dev-qt/qtbase:6 dev-qt/qtdeclarative:6 dev-qt/qtsvg:6 dev-qt/qttools:6` - Optional : To build the flag `WITH_SCANNER` - For Debian distributions (Debian, Ubuntu, Mint, Tails...) - `sudo apt install qtmultimedia5-dev qml-module-qtmultimedia` + `sudo apt install qt6-multimedia-dev qml6-module-qtmultimedia` - For Gentoo - `emerge dev-qt/qtmultimedia:5` + `emerge dev-qt/qtmultimedia:6` 3. Clone repository @@ -262,7 +265,7 @@ The following instructions will fetch Qt from your distribution's repositories i make release ``` - \* Add `CMAKE_PREFIX_PATH` environment variable to set a custom Qt install directory, e.g. `CMAKE_PREFIX_PATH=$HOME/Qt/5.9.7/gcc_64 make release` + \* Add `CMAKE_PREFIX_PATH` environment variable to set a custom Qt install directory, e.g. `CMAKE_PREFIX_PATH=$HOME/Qt/6.8.3/gcc_64 make release` \* Set `CMAKE_BUILD_PARALLEL_LEVEL` to control the number of parallel build jobs, e.g. `CMAKE_BUILD_PARALLEL_LEVEL=4 make release` The executable can be found in the build/release/bin folder. @@ -279,7 +282,7 @@ The executable can be found in the build/release/bin folder. 4. Install Qt: - `brew install qt5` (or download QT 5.12+ from [qt.io](https://www.qt.io/download-open-source/)) + `brew install qt` (or download Qt 6.8+ from [qt.io](https://www.qt.io/download-open-source/)) 5. Grab an up-to-date copy of the monero-gui repository @@ -293,7 +296,7 @@ The executable can be found in the build/release/bin folder. ``` make release ``` - \* Add `CMAKE_PREFIX_PATH` environment variable to set a custom Qt install directory, e.g. `CMAKE_PREFIX_PATH=$HOME/Qt/5.9.7/clang_64 make release` + \* Add `CMAKE_PREFIX_PATH` environment variable to set a custom Qt install directory, e.g. `CMAKE_PREFIX_PATH=$HOME/Qt/6.8.3/macos make release` \* Set `CMAKE_BUILD_PARALLEL_LEVEL` to control the number of parallel build jobs, e.g. `CMAKE_BUILD_PARALLEL_LEVEL=4 make release` The executable can be found in the `build/release/bin` folder. @@ -316,10 +319,10 @@ The Monero GUI on Windows is 64 bits only; 32-bit Windows GUI builds are not off You find more details about those dependencies in the [Monero documentation](https://github.com/monero-project/monero). Note that that there is no more need to compile Boost from source; like everything else, you can install it now with a MSYS2 package. -4. Install Qt5 +4. Install Qt6 ``` - pacman -S mingw-w64-x86_64-qt5 + pacman -S mingw-w64-x86_64-qt6-base mingw-w64-x86_64-qt6-declarative mingw-w64-x86_64-qt6-svg mingw-w64-x86_64-qt6-tools ``` There is no more need to download some special installer from the Qt website, the standard MSYS2 package for Qt will do in almost all circumstances. diff --git a/cmake/DeleteHomebrewRpaths.cmake b/cmake/DeleteHomebrewRpaths.cmake new file mode 100644 index 0000000000..3043cbaaba --- /dev/null +++ b/cmake/DeleteHomebrewRpaths.cmake @@ -0,0 +1,32 @@ +if(NOT DEFINED EXECUTABLE) + message(FATAL_ERROR "EXECUTABLE is not set") +endif() + +if(NOT DEFINED INSTALL_NAME_TOOL) + set(INSTALL_NAME_TOOL install_name_tool) +endif() + +execute_process( + COMMAND otool -l "${EXECUTABLE}" + OUTPUT_VARIABLE _otool_output + RESULT_VARIABLE _otool_result +) + +if(NOT _otool_result EQUAL 0) + message(FATAL_ERROR "Failed to read rpaths from ${EXECUTABLE}") +endif() + +string(REGEX MATCHALL "path (/opt/homebrew|/usr/local)[^ \n]*" _homebrew_rpath_lines "${_otool_output}") + +foreach(_line IN LISTS _homebrew_rpath_lines) + string(REGEX REPLACE "^path " "" _rpath "${_line}") + + message(STATUS "Deleting Homebrew rpath: ${_rpath}") + + execute_process( + COMMAND "${INSTALL_NAME_TOOL}" -delete_rpath "${_rpath}" "${EXECUTABLE}" + RESULT_VARIABLE _delete_result + OUTPUT_QUIET + ERROR_QUIET + ) +endforeach() diff --git a/cmake/Deploy.cmake b/cmake/Deploy.cmake index e5f147f63e..87c1b5c28f 100644 --- a/cmake/Deploy.cmake +++ b/cmake/Deploy.cmake @@ -1,6 +1,6 @@ if(APPLE OR (WIN32 AND NOT STATIC)) add_custom_target(deploy) - get_target_property(_qmake_executable Qt5::qmake IMPORTED_LOCATION) + get_target_property(_qmake_executable Qt6::qmake IMPORTED_LOCATION) get_filename_component(_qt_bin_dir "${_qmake_executable}" DIRECTORY) if(APPLE AND NOT IOS) @@ -11,21 +11,6 @@ if(APPLE OR (WIN32 AND NOT STATIC)) COMMENT "Running macdeployqt..." ) - # workaround for a Qt bug that requires manually adding libqsvg.dylib to bundle - find_file(_qt_svg_dylib "libqsvg.dylib" PATHS "${CMAKE_PREFIX_PATH}/plugins/imageformats" NO_DEFAULT_PATH) - if(_qt_svg_dylib) - add_custom_command(TARGET deploy - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${_qt_svg_dylib} $/../PlugIns/imageformats/ - COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change "${CMAKE_PREFIX_PATH}/lib/QtGui.framework/Versions/5/QtGui" "@executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui" $/../PlugIns/imageformats/libqsvg.dylib - COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change "${CMAKE_PREFIX_PATH}/lib/QtWidgets.framework/Versions/5/QtWidgets" "@executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets" $/../PlugIns/imageformats/libqsvg.dylib - COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change "${CMAKE_PREFIX_PATH}/lib/QtSvg.framework/Versions/5/QtSvg" "@executable_path/../Frameworks/QtSvg.framework/Versions/5/QtSvg" $/../PlugIns/imageformats/libqsvg.dylib - COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change "${CMAKE_PREFIX_PATH}/lib/QtCore.framework/Versions/5/QtCore" "@executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore" $/../PlugIns/imageformats/libqsvg.dylib - COMMENT "Copying libqsvg.dylib, running install_name_tool" - - ) - endif() - # Copy Boost dylibs that macdeployqt doesn't pick up find_package(Boost QUIET COMPONENTS atomic container date_time) set(_boost_extras Boost::atomic Boost::container Boost::date_time) @@ -40,6 +25,71 @@ if(APPLE OR (WIN32 AND NOT STATIC)) endif() endforeach() + # Copy Abseil runtime libraries used by Protobuf's utf8_range libraries. + # Homebrew's libutf8_validity.dylib links against libabsl_*.dylib, and + # macdeployqt does not deploy these because the utf8_range libraries are copied manually. + find_package(Protobuf QUIET) + + if(TARGET protobuf::libprotobuf) + get_target_property(_protobuf_dylib protobuf::libprotobuf IMPORTED_LOCATION) + + if(_protobuf_dylib) + get_filename_component(_protobuf_lib_dir "${_protobuf_dylib}" DIRECTORY) + + file(GLOB _protobuf_utf8_dylibs + "${_protobuf_lib_dir}/libutf8_*.dylib" + ) + + foreach(_dylib IN LISTS _protobuf_utf8_dylibs) + add_custom_command(TARGET deploy POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${_dylib}" + "$/../Frameworks/" + COMMENT "Copying ${_dylib}" + ) + endforeach() + + find_file(_abseil_strings_dylib + NAMES libabsl_strings.dylib + PATHS + /opt/homebrew/opt/abseil/lib + /usr/local/opt/abseil/lib + "${_protobuf_lib_dir}" + NO_DEFAULT_PATH + ) + + if(_abseil_strings_dylib) + get_filename_component(_abseil_lib_dir "${_abseil_strings_dylib}" DIRECTORY) + + file(GLOB _protobuf_abseil_dylibs + "${_abseil_lib_dir}/libabsl_*.dylib" + ) + + foreach(_dylib IN LISTS _protobuf_abseil_dylibs) + add_custom_command(TARGET deploy POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${_dylib}" + "$/../Frameworks/" + COMMENT "Copying ${_dylib}" + ) + endforeach() + else() + message(WARNING "Abseil dylibs not found; libutf8_validity may still reference Homebrew") + endif() + endif() + endif() + + # Remove Homebrew rpaths from the executable. + # These can make the app load local Homebrew libraries instead of bundled libraries. + add_custom_command(TARGET deploy + POST_BUILD + COMMAND ${CMAKE_COMMAND} + -DEXECUTABLE=$ + -DINSTALL_NAME_TOOL=${CMAKE_INSTALL_NAME_TOOL} + -P ${CMAKE_SOURCE_DIR}/cmake/DeleteHomebrewRpaths.cmake + COMMENT "Removing Homebrew rpaths from app executable" + ) + # Apple Silicon requires all binaries to be codesigned find_program(CODESIGN_EXECUTABLE NAMES codesign) if(CODESIGN_EXECUTABLE) @@ -53,11 +103,22 @@ if(APPLE OR (WIN32 AND NOT STATIC)) elseif(WIN32) find_program(QMAKE_EXECUTABLE qmake HINTS "${_qt_bin_dir}") find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${_qt_bin_dir}") - if(NOT QMAKE_EXECUTABLE OR NOT WINDEPLOYQT_EXECUTABLE) - message(WARNING "Deploy requires qmake.exe and windeployqt.exe (no -qt5 suffix) in ${_qt_bin_dir}") + set(QMLIMPORTSCANNER_EXECUTABLE "${_qt_bin_dir}/qmlimportscanner${CMAKE_EXECUTABLE_SUFFIX}") + if(NOT QMAKE_EXECUTABLE OR NOT WINDEPLOYQT_EXECUTABLE OR + NOT EXISTS "${QMLIMPORTSCANNER_EXECUTABLE}") + message(WARNING "Deploy requires qmake, windeployqt, and qmlimportscanner in ${_qt_bin_dir}") + endif() + + execute_process( + COMMAND "${QMAKE_EXECUTABLE}" -query QT_INSTALL_QML + OUTPUT_VARIABLE _qt_qml_dir + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT IS_DIRECTORY "${_qt_qml_dir}") + message(WARNING "Qt QML import directory does not exist: ${_qt_qml_dir}") endif() add_custom_command(TARGET deploy POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E env PATH="${_qt_bin_dir}" "${WINDEPLOYQT_EXECUTABLE}" "$" -no-translations -qmldir="${CMAKE_SOURCE_DIR}" + COMMAND "${CMAKE_COMMAND}" -E env PATH="${_qt_bin_dir}" "${WINDEPLOYQT_EXECUTABLE}" "$" -no-translations -qmldir="${CMAKE_SOURCE_DIR}" -qmlimport="${_qt_qml_dir}" COMMENT "Running windeployqt..." ) set(WIN_DEPLOY_DLLS @@ -74,14 +135,11 @@ if(APPLE OR (WIN32 AND NOT STATIC)) zlib1.dll libzstd.dll libwinpthread-1.dll - libtiff-6.dll libstdc++-6.dll libpng16-16.dll libpcre16-0.dll libpcre-1.dll - libmng-2.dll liblzma-5.dll - liblcms2-2.dll libjpeg-8.dll libintl-8.dll libiconv-2.dll diff --git a/components/AdvancedOptionsItem.qml b/components/AdvancedOptionsItem.qml index d43944e8ad..1b6976c259 100644 --- a/components/AdvancedOptionsItem.qml +++ b/components/AdvancedOptionsItem.qml @@ -1,5 +1,5 @@ -import QtQuick 2.9 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Layouts import "../components" as MoneroComponents diff --git a/components/CheckBox.qml b/components/CheckBox.qml index d0615e0f10..aa2534f189 100644 --- a/components/CheckBox.qml +++ b/components/CheckBox.qml @@ -26,9 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.1 -import FontAwesome 1.0 +import QtQuick +import QtQuick.Layouts + +import FontAwesome import "." as MoneroComponents import "effects/" as MoneroEffects @@ -57,8 +58,8 @@ Item { opacity: enabled ? 1 : 0.7 Keys.onEnterPressed: toggle() - Keys.onReturnPressed: Keys.onEnterPressed(event) - Keys.onSpacePressed: Keys.onEnterPressed(event) + Keys.onReturnPressed: toggle() + Keys.onSpacePressed: toggle() function toggle(){ if (checkBox.toggleOnClick) { diff --git a/components/CheckBox2.qml b/components/CheckBox2.qml index 3764b99fa3..e64b9be7bf 100644 --- a/components/CheckBox2.qml +++ b/components/CheckBox2.qml @@ -26,10 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.1 -import QtGraphicalEffects 1.0 -import FontAwesome 1.0 +import QtQuick +import QtQuick.Layouts + +import FontAwesome import "." 1.0 import "." as MoneroComponents diff --git a/components/ContextMenu.qml b/components/ContextMenu.qml index efbd38ab07..c7e9100dbb 100644 --- a/components/ContextMenu.qml +++ b/components/ContextMenu.qml @@ -1,7 +1,6 @@ -import QtQuick 2.9 -import QtQuick.Controls 2.2 +import QtQuick +import QtQuick.Controls -import FontAwesome 1.0 import "../components" as MoneroComponents MouseArea { @@ -14,7 +13,7 @@ MouseArea { id: root acceptedButtons: Qt.RightButton anchors.fill: parent - onClicked: { + onClicked: (mouse) => { if (mouse.button === Qt.RightButton) { root.parent.persistentSelection = true; contextMenu.open() diff --git a/components/ContextMenuItem.qml b/components/ContextMenuItem.qml index c4e0f39512..0b74931302 100644 --- a/components/ContextMenuItem.qml +++ b/components/ContextMenuItem.qml @@ -1,8 +1,9 @@ -import QtQuick 2.9 -import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import FontAwesome -import FontAwesome 1.0 import "../components" as MoneroComponents MenuItem { diff --git a/components/DaemonManagerDialog.qml b/components/DaemonManagerDialog.qml index 89e59f8978..f9804b8f43 100644 --- a/components/DaemonManagerDialog.qml +++ b/components/DaemonManagerDialog.qml @@ -26,12 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 1.4 -import QtQuick.Dialogs 1.2 -import QtQuick.Layouts 1.1 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Window 2.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Window import moneroComponents.Wallet 1.0 import "../components" as MoneroComponents diff --git a/components/DatePicker.qml b/components/DatePicker.qml index a9eba5f4cc..214aaa26bc 100644 --- a/components/DatePicker.qml +++ b/components/DatePicker.qml @@ -26,13 +26,11 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 1.2 -import QtQuick.Controls 2.2 as QtQuickControls2 -import QtQuick.Layouts 1.2 -import QtGraphicalEffects 1.0 -import QtQuick.Controls.Styles 1.2 -import FontAwesome 1.0 +import QtQuick +import QtQuick.Controls as Controls +import QtQuick.Layouts + +import FontAwesome import "." as MoneroComponents import "effects/" as MoneroEffects @@ -47,7 +45,7 @@ Item { property bool error: false property alias inputLabel: inputLabel - signal dateChanged(); + signal dateChanged() height: 50 @@ -246,10 +244,10 @@ Item { } } - QtQuickControls2.Popup { + Controls.Popup { id: popup padding: 0 - closePolicy: QtQuickControls2.Popup.CloseOnEscape | QtQuickControls2.Popup.CloseOnPressOutsideParent + closePolicy: Controls.Popup.CloseOnEscape | Controls.Popup.CloseOnPressOutsideParent onOpened: { calendar.visibleMonth = currentDate.getMonth(); calendar.visibleYear = currentDate.getFullYear(); @@ -290,7 +288,7 @@ Item { height: 1 } - Calendar { + MoneroComponents.MoneroCalendar { id: calendar anchors.left: parent.left anchors.right: parent.right @@ -298,166 +296,11 @@ Item { anchors.margins: 1 anchors.bottomMargin: 10 height: 220 - frameVisible: false - - style: CalendarStyle { - gridVisible: false - background: Rectangle { color: MoneroComponents.Style.middlePanelBackgroundColor } - dayDelegate: Item { - z: parent.z + 1 - implicitHeight: implicitWidth - implicitWidth: calendar.width / 7 - - Rectangle { - id: dayRect - anchors.fill: parent - radius: parent.implicitHeight / 2 - } - - MoneroComponents.TextPlain { - id: dayText - anchors.centerIn: parent - font.family: MoneroComponents.Style.fontMonoRegular.name - font.pixelSize: { - if(!styleData.visibleMonth) return 12 - return 14 - } - font.bold: { - if(dayArea.pressed || styleData.visibleMonth) return true; - return false; - } - text: styleData.date.getDate() - themeTransition: false - color: { - if (currentDate.toDateString() === styleData.date.toDateString()) { - if (dayArea.containsMouse) { - dayRect.color = MoneroComponents.Style.buttonBackgroundColorHover; - } else { - dayRect.color = MoneroComponents.Style.buttonBackgroundColor; - } - } else { - if (dayArea.containsMouse) { - dayRect.color = MoneroComponents.Style.blackTheme ? "#20FFFFFF" : "#10000000" - } else { - dayRect.color = "transparent"; - } - } - if(!styleData.valid) return "transparent" - if(styleData.date.toDateString() === (new Date()).toDateString()) return "#FFFF00" - if(!styleData.visibleMonth) return MoneroComponents.Style.lightGreyFontColor - if(dayArea.pressed) return MoneroComponents.Style.defaultFontColor - return MoneroComponents.Style.defaultFontColor - } - } - - MouseArea { - id: dayArea - anchors.fill: parent - visible: styleData.valid - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - if(styleData.visibleMonth) { - currentDate = styleData.date - popup.close() - } else { - var date = styleData.date - if(date.getMonth() > calendar.visibleMonth) - calendar.showNextMonth() - else calendar.showPreviousMonth() - } - - datePicker.dateChanged(); - } - } - } - - dayOfWeekDelegate: Item { - implicitHeight: 20 - implicitWidth: calendar.width / 7 - - MoneroComponents.TextPlain { - anchors.centerIn: parent - elide: Text.ElideRight - font.family: MoneroComponents.Style.fontMonoRegular.name - font.pixelSize: 12 - color: MoneroComponents.Style.lightGreyFontColor - themeTransition: false - text: { - var locale = Qt.locale() - return locale.dayName(styleData.dayOfWeek, Locale.ShortFormat) - } - } - } - - navigationBar: Rectangle { - color: MoneroComponents.Style.middlePanelBackgroundColor - implicitWidth: calendar.width - implicitHeight: 30 - - MoneroComponents.TextPlain { - anchors.centerIn: parent - font.family: MoneroComponents.Style.fontMonoRegular.name - font.pixelSize: 14 - color: MoneroComponents.Style.dimmedFontColor - themeTransition: false - text: styleData.title - } - - - Item { - anchors.left: parent.left - anchors.leftMargin: 4 - anchors.top: parent.top - anchors.bottom: parent.bottom - width: height - - MoneroEffects.ImageMask { - id: prevMonthIcon - anchors.centerIn: parent - image: "qrc:///images/prevMonth.png" - height: 8 - width: 12 - fontAwesomeFallbackIcon: FontAwesome.arrowLeft - fontAwesomeFallbackSize: 14 - color: MoneroComponents.Style.defaultFontColor - } - - MouseArea { - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - anchors.fill: parent - onClicked: calendar.showPreviousMonth() - } - } - - Item { - anchors.right: parent.right - anchors.rightMargin: 4 - anchors.top: parent.top - anchors.bottom: parent.bottom - width: height - - MoneroEffects.ImageMask { - id: nextMonthIcon - anchors.centerIn: parent - image: "qrc:///images/prevMonth.png" - height: 8 - width: 12 - rotation: 180 - fontAwesomeFallbackIcon: FontAwesome.arrowLeft - fontAwesomeFallbackSize: 14 - color: MoneroComponents.Style.defaultFontColor - } - - MouseArea { - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - anchors.fill: parent - onClicked: calendar.showNextMonth() - } - } - } + selectedDate: datePicker.currentDate + onDateSelected: function(selectedDate) { + datePicker.currentDate = selectedDate + popup.close() + datePicker.dateChanged() } } } diff --git a/components/DevicePassphraseDialog.qml b/components/DevicePassphraseDialog.qml index 75db398834..ff83b155ae 100644 --- a/components/DevicePassphraseDialog.qml +++ b/components/DevicePassphraseDialog.qml @@ -26,7 +26,7 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 +import QtQuick import "." as MoneroComponents Item { diff --git a/components/Dialog.qml b/components/Dialog.qml index 0cd712facd..7788f9cfa7 100644 --- a/components/Dialog.qml +++ b/components/Dialog.qml @@ -26,9 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts import "." as MoneroComponents diff --git a/components/IconButton.qml b/components/IconButton.qml index 334f0da5e0..25d5d03e6f 100644 --- a/components/IconButton.qml +++ b/components/IconButton.qml @@ -26,7 +26,7 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 +import QtQuick import "../components" as MoneroComponents import "../components/effects" as MoneroEffects @@ -62,6 +62,6 @@ MoneroEffects.ImageMask { button.height = button.height - 2 } - onClicked: button.clicked(mouse) + onClicked: (mouse) => button.clicked(mouse) } } diff --git a/components/InlineButton.qml b/components/InlineButton.qml index 3fd75538b3..0f95a3b455 100644 --- a/components/InlineButton.qml +++ b/components/InlineButton.qml @@ -26,15 +26,17 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.1 -import QtGraphicalEffects 1.0 +import QtQuick +import QtQuick.Layouts -import FontAwesome 1.0 + +import FontAwesome import "." as MoneroComponents import "./effects/" as MoneroEffects +import QtQuick.Effects + Item { id: inlineButton @@ -42,9 +44,9 @@ Item { property string textColor: MoneroComponents.Style.inlineButtonTextColor property alias text: inlineText.text property alias fontPixelSize: inlineText.font.pixelSize - property alias fontFamily: inlineText.font.family - property alias fontStyleName: inlineText.font.styleName - property bool isFontAwesomeIcon: fontFamily == FontAwesome.fontFamily || fontFamily == FontAwesome.fontFamilySolid + property string fontFamily: MoneroComponents.Style.fontBold.name + property string fontStyleName: "" + property bool isFontAwesomeIcon: fontStyleName === "Solid" || fontFamily === FontAwesome.fontFamily || fontFamily === FontAwesome.fontFamilySolid property alias buttonColor: rect.color property alias tooltip: tooltip.text property alias tooltipLeft: tooltip.tooltipLeft @@ -70,8 +72,9 @@ Item { MoneroComponents.TextPlain { id: inlineText - font.family: MoneroComponents.Style.fontBold.name - font.bold: true + font.family: inlineButton.fontStyleName === "Solid" ? FontAwesome.fontFamilySolid : inlineButton.fontFamily + font.styleName: inlineButton.fontStyleName + font.bold: !inlineButton.isFontAwesomeIcon font.pixelSize: inlineButton.isFontAwesomeIcon ? 22 : inlineButton.small ? 14 : 16 color: inlineButton.textColor anchors.verticalCenter: parent.verticalCenter @@ -108,20 +111,20 @@ Item { } } - DropShadow { - visible: !MoneroComponents.Style.blackTheme + MultiEffect { + visible: !MoneroComponents.Style.blackTheme && GraphicsInfo.api !== GraphicsInfo.Software anchors.fill: rect - horizontalOffset: 2 - verticalOffset: 2 - radius: 7.0 - samples: 10 - color: "#1B000000" - cached: true source: rect + shadowEnabled: true + shadowHorizontalOffset: 2 + shadowVerticalOffset: 2 + shadowBlur: 0.875 + blurMax: 16 + shadowColor: "#1B000000" } Keys.enabled: inlineButton.visible Keys.onSpacePressed: doClick() - Keys.onEnterPressed: Keys.onReturnPressed(event) + Keys.onEnterPressed: doClick() Keys.onReturnPressed: doClick() } diff --git a/components/Input.qml b/components/Input.qml index 3fb83af1ce..4d0bbe4622 100644 --- a/components/Input.qml +++ b/components/Input.qml @@ -26,8 +26,8 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick.Controls 2.0 -import QtQuick 2.9 +import QtQuick.Controls +import QtQuick import "../components" as MoneroComponents diff --git a/components/InputDialog.qml b/components/InputDialog.qml index 40e3e4452f..49ea38abcc 100644 --- a/components/InputDialog.qml +++ b/components/InputDialog.qml @@ -26,12 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 2.0 -import QtQuick.Dialogs 1.2 -import QtQuick.Layouts 1.1 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Window 2.0 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts import "../components" as MoneroComponents @@ -109,11 +106,15 @@ Item { } Keys.enabled: root.visible - Keys.onEnterPressed: Keys.onReturnPressed(event) - Keys.onReturnPressed: { + function acceptInput() { root.close() root.accepted() } + + Keys.onEnterPressed: acceptInput() + Keys.onReturnPressed: { + acceptInput() + } Keys.onEscapePressed: { root.close() root.rejected() diff --git a/components/InputMulti.qml b/components/InputMulti.qml index 6f07dd3aaf..f6f9ed68b3 100644 --- a/components/InputMulti.qml +++ b/components/InputMulti.qml @@ -26,8 +26,8 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick.Controls 2.0 -import QtQuick 2.9 +import QtQuick.Controls +import QtQuick import "../js/TxUtils.js" as TxUtils import "../components" as MoneroComponents @@ -51,12 +51,16 @@ TextArea { selectionColor: MoneroComponents.Style.textSelectionColor selectedTextColor: MoneroComponents.Style.textSelectedColor + background: Rectangle { + color: "transparent" + } + property int minimumHeight: 100 height: contentHeight > minimumHeight ? contentHeight : minimumHeight onTextChanged: { if(addressValidation){ - // js replacement for `RegExpValidator { regExp: /[0-9A-Fa-f]{95}/g }` + // js replacement for `RegularExpressionValidator { regularExpression: /[0-9A-Fa-f]{95}/g }` if (textArea.text.startsWith("monero:")) { error = false; return; diff --git a/components/Label.qml b/components/Label.qml index 2277811348..f9fda12878 100644 --- a/components/Label.qml +++ b/components/Label.qml @@ -26,8 +26,8 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Layouts import "../components" as MoneroComponents diff --git a/components/LabelButton.qml b/components/LabelButton.qml index b7a4065174..abc1af3fb5 100644 --- a/components/LabelButton.qml +++ b/components/LabelButton.qml @@ -26,8 +26,8 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Layouts import "../components" as MoneroComponents diff --git a/components/LabelSubheader.qml b/components/LabelSubheader.qml index a7432c5ad6..cf142923ae 100644 --- a/components/LabelSubheader.qml +++ b/components/LabelSubheader.qml @@ -26,7 +26,7 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 +import QtQuick import "../components" as MoneroComponents import "../components/effects/" as MoneroEffects diff --git a/components/LanguageButton.qml b/components/LanguageButton.qml index aa33ea854d..a57ded940f 100644 --- a/components/LanguageButton.qml +++ b/components/LanguageButton.qml @@ -26,10 +26,11 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.3 +import QtQuick +import QtQuick.Layouts -import FontAwesome 1.0 + +import FontAwesome import "../components" as MoneroComponents diff --git a/components/LanguageSidebar.qml b/components/LanguageSidebar.qml index 20712a9125..cfef1786dd 100644 --- a/components/LanguageSidebar.qml +++ b/components/LanguageSidebar.qml @@ -28,10 +28,10 @@ import "../components" as MoneroComponents -import QtQuick 2.9 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 -import moneroComponents.LanguageModel 1.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import moneroComponents.LanguageModel 1.0 as LanguageModels Drawer { @@ -85,7 +85,6 @@ Drawer { var locale_spl = locale.split("_"); // reload active translations - console.log(locale_spl[0]); translationManager.setLanguage(locale_spl[0]); // set wizard language settings @@ -170,8 +169,7 @@ Drawer { } } - //Flags model - LanguageModel { + LanguageModels.LanguageModel { id: langModel } diff --git a/components/LineEdit.qml b/components/LineEdit.qml index 0274103d0f..22b3673829 100644 --- a/components/LineEdit.qml +++ b/components/LineEdit.qml @@ -26,10 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import FontAwesome 1.0 -import QtQuick 2.9 -import QtGraphicalEffects 1.0 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Layouts + +import FontAwesome import "../components" as MoneroComponents diff --git a/components/LineEditMulti.qml b/components/LineEditMulti.qml index d937b6ddd3..68f064f345 100644 --- a/components/LineEditMulti.qml +++ b/components/LineEditMulti.qml @@ -26,8 +26,8 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Layouts import "../components" as MoneroComponents diff --git a/components/MenuBar.qml b/components/MenuBar.qml index e605f01ed9..769d65ae2f 100644 --- a/components/MenuBar.qml +++ b/components/MenuBar.qml @@ -26,7 +26,7 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import Qt.labs.platform 1.0 as PlatformLabs +import Qt.labs.platform as PlatformLabs import "." as MoneroComponents PlatformLabs.MenuBar { diff --git a/components/MenuButton.qml b/components/MenuButton.qml index 5f6381f39c..b39bd3de96 100644 --- a/components/MenuButton.qml +++ b/components/MenuButton.qml @@ -26,8 +26,7 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtGraphicalEffects 1.0 +import QtQuick import "../components" as MoneroComponents import "effects/" as MoneroEffects @@ -61,26 +60,25 @@ Rectangle { property bool present: !under || under.checked || checked || under.numSelectedChildren > 0 height: present ? ((appWindow.height >= 800) ? 44 : 38 ) : 0 - LinearGradient { - visible: isOpenGL && (button.checked || buttonArea.containsMouse) + Rectangle { + visible: GraphicsInfo.api !== GraphicsInfo.Software && (button.checked || buttonArea.containsMouse) height: parent.height width: 260 anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: -20 anchors.leftMargin: parent.getOffset() - start: Qt.point(width, 0) - end: Qt.point(0, 0) gradient: Gradient { - GradientStop { position: 0.0; color: MoneroComponents.Style.menuButtonGradientStart } - GradientStop { position: 1.0; color: MoneroComponents.Style.menuButtonGradientStop } + orientation: Gradient.Horizontal + GradientStop { position: 0.0; color: MoneroComponents.Style.menuButtonGradientStop } + GradientStop { position: 1.0; color: MoneroComponents.Style.menuButtonGradientStart } } opacity: button.checked ? 1 : 0.3 } - // fallback hover effect when opengl is not available + // fallback hover effect for the software renderer Rectangle { - visible: !isOpenGL && (button.checked || buttonArea.containsMouse) + visible: GraphicsInfo.api === GraphicsInfo.Software && (button.checked || buttonArea.containsMouse) anchors.fill: parent color: MoneroComponents.Style.menuButtonFallbackBackgroundColor opacity: button.checked ? 1 : 0.3 diff --git a/components/MenuButtonDivider.qml b/components/MenuButtonDivider.qml index 50d7c0f499..7b3de5bc23 100644 --- a/components/MenuButtonDivider.qml +++ b/components/MenuButtonDivider.qml @@ -1,4 +1,4 @@ -import QtQuick 2.9 +import QtQuick import "." as MoneroComponents import "effects/" as MoneroEffects diff --git a/components/MoneroCalendar.qml b/components/MoneroCalendar.qml new file mode 100644 index 0000000000..b432f0b6d5 --- /dev/null +++ b/components/MoneroCalendar.qml @@ -0,0 +1,194 @@ +// Copyright (c) 2026, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import QtQuick +import QtQuick.Controls + +import "." as MoneroComponents + +Item { + id: root + + property int visibleMonth: (new Date()).getMonth() + property int visibleYear: (new Date()).getFullYear() + property date selectedDate + + signal dateSelected(date selectedDate) + + function moveMonth(offset) { + const date = new Date(visibleYear, visibleMonth + offset, 1) + visibleMonth = date.getMonth() + visibleYear = date.getFullYear() + } + + function showPreviousMonth() { + moveMonth(-1) + } + + function showNextMonth() { + moveMonth(1) + } + + Rectangle { + anchors.fill: parent + color: MoneroComponents.Style.middlePanelBackgroundColor + + Column { + anchors.fill: parent + + Rectangle { + width: parent.width + height: 30 + color: MoneroComponents.Style.middlePanelBackgroundColor + + MoneroComponents.TextPlain { + anchors.centerIn: parent + font.family: MoneroComponents.Style.fontMonoRegular.name + font.pixelSize: 14 + color: MoneroComponents.Style.dimmedFontColor + themeTransition: false + text: Qt.locale().standaloneMonthName(root.visibleMonth, Locale.LongFormat) + + " " + root.visibleYear + } + + MoneroComponents.TextPlain { + anchors.left: parent.left + anchors.leftMargin: 12 + anchors.verticalCenter: parent.verticalCenter + text: "\u2039" + font.pixelSize: 22 + color: MoneroComponents.Style.defaultFontColor + + MouseArea { + anchors.fill: parent + anchors.margins: -8 + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: root.showPreviousMonth() + } + } + + MoneroComponents.TextPlain { + anchors.right: parent.right + anchors.rightMargin: 12 + anchors.verticalCenter: parent.verticalCenter + text: "\u203a" + font.pixelSize: 22 + color: MoneroComponents.Style.defaultFontColor + + MouseArea { + anchors.fill: parent + anchors.margins: -8 + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: root.showNextMonth() + } + } + } + + DayOfWeekRow { + width: parent.width + height: 20 + locale: Qt.locale() + + delegate: MoneroComponents.TextPlain { + required property var model + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font.family: MoneroComponents.Style.fontMonoRegular.name + font.pixelSize: 12 + color: MoneroComponents.Style.lightGreyFontColor + themeTransition: false + text: model.shortName + } + } + + MonthGrid { + id: monthGrid + width: parent.width + height: parent.height - 50 + month: root.visibleMonth + year: root.visibleYear + locale: Qt.locale() + + delegate: Item { + required property var model + + Rectangle { + id: dayBackground + anchors.centerIn: parent + width: Math.min(parent.width, parent.height) + height: width + radius: width / 2 + color: { + if (root.selectedDate + && root.selectedDate.toDateString() === model.date.toDateString()) + return MoneroComponents.Style.buttonBackgroundColor + if (dayArea.containsMouse) + return MoneroComponents.Style.blackTheme ? "#20FFFFFF" : "#10000000" + return "transparent" + } + } + + MoneroComponents.TextPlain { + anchors.centerIn: parent + font.family: MoneroComponents.Style.fontMonoRegular.name + font.pixelSize: model.month === root.visibleMonth ? 14 : 12 + font.bold: model.month === root.visibleMonth + color: { + if (model.today) + return "#FFFF00" + if (model.month !== root.visibleMonth) + return MoneroComponents.Style.lightGreyFontColor + return MoneroComponents.Style.defaultFontColor + } + text: model.day + themeTransition: false + } + + MouseArea { + id: dayArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + if (model.month !== root.visibleMonth) { + if (model.date < new Date(root.visibleYear, root.visibleMonth, 1)) + root.showPreviousMonth() + else + root.showNextMonth() + return + } + root.dateSelected(model.date) + } + } + } + } + } + } +} diff --git a/components/Navbar.qml b/components/Navbar.qml index 3383d5604a..be3dba2069 100644 --- a/components/Navbar.qml +++ b/components/Navbar.qml @@ -26,8 +26,8 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Layouts import "." as MoneroComponents Rectangle { diff --git a/components/NavbarItem.qml b/components/NavbarItem.qml index b1a562393f..937dfe3097 100644 --- a/components/NavbarItem.qml +++ b/components/NavbarItem.qml @@ -26,7 +26,7 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 +import QtQuick QtObject { property bool active: false diff --git a/components/NetworkStatusItem.qml b/components/NetworkStatusItem.qml index c22a4b2d8c..87eae34696 100644 --- a/components/NetworkStatusItem.qml +++ b/components/NetworkStatusItem.qml @@ -26,10 +26,11 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Layouts + +import FontAwesome -import FontAwesome 1.0 import moneroComponents.Wallet 1.0 import "../components" as MoneroComponents diff --git a/components/PasswordDialog.qml b/components/PasswordDialog.qml index 90e6b00e63..3816d6b93b 100644 --- a/components/PasswordDialog.qml +++ b/components/PasswordDialog.qml @@ -26,17 +26,11 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 2.0 -import QtQuick.Dialogs 1.2 -import QtQuick.Layouts 1.1 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Window 2.0 -import FontAwesome 1.0 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts import "." as MoneroComponents -import "effects/" as MoneroEffects -import "../js/Utils.js" as Utils FocusScope { id: root diff --git a/components/ProcessingSplash.qml b/components/ProcessingSplash.qml index 469cc1fad8..309b40c2b1 100644 --- a/components/ProcessingSplash.qml +++ b/components/ProcessingSplash.qml @@ -26,11 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Window 2.1 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Window +import QtQuick.Controls +import QtQuick.Layouts import "../components" as MoneroComponents @@ -73,25 +72,24 @@ Rectangle { id: imgLogo width: 60 height: 60 + sourceSize: Qt.size(width, height) anchors.centerIn: parent source: "qrc:///images/monero-vector.svg" mipmap: true } BusyIndicator { - running: parent.visible + running: root.visible anchors.centerIn: imgLogo - style: BusyIndicatorStyle { - indicator: Image { - visible: control.running - source: "qrc:///images/busy-indicator.png" - RotationAnimator on rotation { - running: control.running - loops: Animation.Infinite - duration: 1000 - from: 0 - to: 360 - } + contentItem: Image { + visible: parent.running + source: "qrc:///images/busy-indicator.png" + RotationAnimator on rotation { + running: root.visible + loops: Animation.Infinite + duration: 1000 + from: 0 + to: 360 } } } diff --git a/components/ProgressBar.qml b/components/ProgressBar.qml index 7600d68344..b31b52c9b8 100644 --- a/components/ProgressBar.qml +++ b/components/ProgressBar.qml @@ -26,7 +26,7 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 +import QtQuick import moneroComponents.Wallet 1.0 import "../components" as MoneroComponents diff --git a/components/QRCodeScanner.qml b/components/QRCodeScanner.qml index 7479c75f6a..23150c0923 100644 --- a/components/QRCodeScanner.qml +++ b/components/QRCodeScanner.qml @@ -26,9 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtMultimedia 5.4 -import QtQuick.Dialogs 1.2 +import QtQuick +import QtCore +import QtMultimedia +import QtQuick.Dialogs import moneroComponents.QRCodeScanner 1.0 Rectangle { @@ -45,17 +46,56 @@ Rectangle { state: "Stopped" signal qrcode_decoded(string address, string payment_id, string amount, string tx_description, string recipient_name, var extra_parameters) + property bool sessionConfigured: false + + function showDecodeError(error, warning) { + if (!warning) + root.state = "Stopped" + messageDialog.text = error + messageDialog.visible = true + } + + function startCamera() { + if (mediaDevices.videoInputs.length === 0) { + appWindow.qrScannerEnabled = false + root.state = "Stopped" + return + } + if (!sessionConfigured) { + if (!finder.setSource(camera) || !finder.setVideoOutput(viewfinder)) { + appWindow.qrScannerEnabled = false + root.state = "Stopped" + return + } + sessionConfigured = true + } + camera.start() + } + + CameraPermission { + id: cameraPermission + } + + MediaDevices { + id: mediaDevices + + onVideoInputsChanged: { + appWindow.qrScannerEnabled = videoInputs.length > 0 + if (videoInputs.length === 0 && root.state === "Capture") + root.state = "Stopped" + } + } states: [ State { name: "Capture" StateChangeScript { script: { - root.visible = true - camera.captureMode = Camera.CaptureStillImage - camera.cameraState = Camera.ActiveState - camera.start() - finder.enabled = true + if (cameraPermission.status !== Qt.PermissionStatus.Granted) { + cameraPermission.request() + return + } + startCamera() } } }, @@ -66,7 +106,6 @@ Rectangle { camera.stop() root.visible = false finder.enabled = false - camera.cameraState = Camera.UnloadedState } } } @@ -75,17 +114,32 @@ Rectangle { Camera { id: camera objectName: "qrCameraQML" - captureMode: Camera.CaptureStillImage - cameraState: Camera.UnloadedState + cameraDevice: mediaDevices.defaultVideoInput - focus { - focusMode: Camera.FocusContinuous + onActiveChanged: { + if (camera.active && root.state === "Capture") { + root.visible = true + finder.enabled = true + } else if (!camera.active) { + root.visible = false + finder.enabled = false + } + } + onErrorOccurred: function(error, errorString) { + console.error("QR scanner camera error:", error, errorString) + if (root.state === "Capture") { + root.state = "Stopped" + messageDialog.text = errorString + messageDialog.visible = true + } } + focusMode: Camera.FocusModeAuto } + QRCodeScanner { id : finder objectName: "QrFinder" - onDecoded : { + onDecoded : (data) => { const parsed = walletManager.parse_uri_to_object(data); if (!parsed.error) { root.qrcode_decoded(parsed.address, parsed.payment_id, parsed.amount, parsed.tx_description, parsed.recipient_name, parsed.extra_parameters); @@ -94,24 +148,17 @@ Rectangle { root.qrcode_decoded(data, "", "", "", "", null); root.state = "Stopped"; } else { - onNotifyError(parsed.error); + root.showDecodeError(parsed.error, false) } } - onNotifyError : { - if( warning ) - messageDialog.icon = StandardIcon.Critical - else { - messageDialog.icon = StandardIcon.Warning - root.state = "Stopped" - } - messageDialog.text = error - messageDialog.visible = true + onNotifyError : (error, warning) => { + root.showDecodeError(error, warning) } } VideoOutput { id: viewfinder - visible: root.state == "Capture" + visible: camera.active x: 0 y: 0 @@ -119,16 +166,9 @@ Rectangle { width: parent.width height: parent.height - source: camera - autoOrientation: true - MouseArea { anchors.fill: parent propagateComposedEvents: true - onPressAndHold: { - if (camera.lockStatus == Camera.locked)camera.unlock() - camera.searchAndLock() - } onDoubleClicked: { root.state = "Stopped" } @@ -137,16 +177,26 @@ Rectangle { MessageDialog { id: messageDialog - title: qsTr("QrCode Scanned") + translationManager.emptyString + title: qsTr("QR Scanner") + translationManager.emptyString onAccepted: { root.state = "Stopped" } } - Component.onCompleted: { - if( QtMultimedia.availableCameras.length == 0) { - console.log("No camera available. Disable qrScannerEnabled"); - appWindow.qrScannerEnabled = false; + Connections { + target: cameraPermission + function onStatusChanged() { + if (cameraPermission.status === Qt.PermissionStatus.Granted && root.state === "Capture") { + startCamera() + } else if (cameraPermission.status === Qt.PermissionStatus.Denied && root.state === "Capture") { + root.state = "Stopped" + messageDialog.text = qsTr("Camera permission was denied.") + translationManager.emptyString + messageDialog.visible = true + } } } + + Component.onCompleted: { + appWindow.qrScannerEnabled = mediaDevices.videoInputs.length > 0 + } } diff --git a/components/RadioButton.qml b/components/RadioButton.qml index 465477bc44..c5512de0fb 100644 --- a/components/RadioButton.qml +++ b/components/RadioButton.qml @@ -26,8 +26,8 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Layouts import "../components" as MoneroComponents diff --git a/components/RemoteNodeDialog.qml b/components/RemoteNodeDialog.qml index 015f122dd3..5bab92e800 100644 --- a/components/RemoteNodeDialog.qml +++ b/components/RemoteNodeDialog.qml @@ -26,9 +26,8 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Layouts import "." as MoneroComponents diff --git a/components/RemoteNodeEdit.qml b/components/RemoteNodeEdit.qml index e6ce47f270..cb311b63b0 100644 --- a/components/RemoteNodeEdit.qml +++ b/components/RemoteNodeEdit.qml @@ -26,12 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick.Controls 1.2 -import QtQuick.Controls.Styles 1.2 -import QtQuick 2.9 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Layouts -import "../js/Utils.js" as Utils import "../components" as MoneroComponents GridLayout { diff --git a/components/RemoteNodeList.qml b/components/RemoteNodeList.qml index f1779e0cfe..73f5c2b6ff 100644 --- a/components/RemoteNodeList.qml +++ b/components/RemoteNodeList.qml @@ -26,10 +26,11 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Layouts -import FontAwesome 1.0 + +import FontAwesome import "." as MoneroComponents import "effects/" as MoneroEffects @@ -138,7 +139,8 @@ ColumnLayout { MoneroComponents.InlineButton { buttonColor: "transparent" - fontFamily: FontAwesome.fontFamily + fontFamily: FontAwesome.fontFamilySolid + fontStyleName: "Solid" fontPixelSize: 18 text: FontAwesome.edit tooltip: qsTr("Edit remote node") + translationManager.emptyString @@ -153,7 +155,8 @@ ColumnLayout { MoneroComponents.InlineButton { buttonColor: "transparent" - fontFamily: FontAwesome.fontFamily + fontFamily: FontAwesome.fontFamilySolid + fontStyleName: "Solid" text: FontAwesome.times visible: remoteNodesModel.count > 1 tooltip: qsTr("Remove remote node") + translationManager.emptyString diff --git a/components/SettingsListItem.qml b/components/SettingsListItem.qml index 7c778704e6..f87b836b82 100644 --- a/components/SettingsListItem.qml +++ b/components/SettingsListItem.qml @@ -1,6 +1,7 @@ -import QtQuick 2.9 -import QtQuick.Layouts 1.1 -import FontAwesome 1.0 +import QtQuick +import QtQuick.Layouts + +import FontAwesome import "../components" as MoneroComponents @@ -11,7 +12,6 @@ ColumnLayout { property alias description: area.text property alias title: header.text property bool isLast: false - property bool enabled: true signal clicked() Layout.fillWidth: true diff --git a/components/Slider.qml b/components/Slider.qml index df3b3cb39a..76ff02a70a 100644 --- a/components/Slider.qml +++ b/components/Slider.qml @@ -1,6 +1,6 @@ -import QtQuick 2.9 -import QtQuick.Controls 2.0 as QtQuickControls -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Controls as QtQuickControls +import QtQuick.Layouts import "../components" as MoneroComponents diff --git a/components/StandardButton.qml b/components/StandardButton.qml index 0df4419458..d44f846a4b 100644 --- a/components/StandardButton.qml +++ b/components/StandardButton.qml @@ -26,10 +26,11 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Layouts -import FontAwesome 1.0 + +import FontAwesome import "../components" as MoneroComponents @@ -187,6 +188,6 @@ Item { Keys.enabled: button.visible Keys.onSpacePressed: doClick() - Keys.onEnterPressed: Keys.onReturnPressed(event) + Keys.onEnterPressed: doClick() Keys.onReturnPressed: doClick() } diff --git a/components/StandardDialog.qml b/components/StandardDialog.qml index 6f515f5180..7f76b46c3d 100644 --- a/components/StandardDialog.qml +++ b/components/StandardDialog.qml @@ -26,12 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 2.0 -import QtQuick.Dialogs 1.2 -import QtQuick.Layouts 1.1 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Window 2.0 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts import "../components" as MoneroComponents import "effects/" as MoneroEffects @@ -50,8 +47,6 @@ Rectangle { property alias cancelText: cancelButton.text property alias closeVisible: closeButton.visible - property var icon - // same signals as Dialog has signal accepted() signal rejected() @@ -145,6 +140,7 @@ Rectangle { selectByMouse: false wrapMode: TextEdit.Wrap color: MoneroComponents.Style.defaultFontColor + background: null MouseArea { anchors.fill: parent diff --git a/components/StandardDropdown.qml b/components/StandardDropdown.qml index f7bf831bbf..3a91ca0e27 100644 --- a/components/StandardDropdown.qml +++ b/components/StandardDropdown.qml @@ -26,11 +26,11 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 2.2 -import QtGraphicalEffects 1.0 -import FontAwesome 1.0 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import FontAwesome import "../components" as MoneroComponents import "../components/effects/" as MoneroEffects diff --git a/components/Style.qml b/components/Style.qml index 5a833f8479..e9610a5d83 100644 --- a/components/Style.qml +++ b/components/Style.qml @@ -1,6 +1,6 @@ pragma Singleton -import QtQuick 2.5 +import QtQuick QtObject { property bool blackTheme: true diff --git a/components/SuccessfulTxDialog.qml b/components/SuccessfulTxDialog.qml index bd59b7ef77..7d627334e5 100644 --- a/components/SuccessfulTxDialog.qml +++ b/components/SuccessfulTxDialog.qml @@ -26,9 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 2.0 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts import moneroComponents.Clipboard 1.0 import "../components" as MoneroComponents @@ -54,14 +54,16 @@ Rectangle { Clipboard { id: clipboard } - property var transactionID; + property string transactionID: "" // same signals as Dialog has signal accepted() signal rejected() - function open(txid) { - root.transactionID = txid; + function open(txids) { + root.transactionID = !txids ? "" + : typeof txids === "string" ? txids + : txids.join("\n"); root.visible = true; } @@ -128,7 +130,7 @@ Rectangle { readOnly: true wrapMode: Text.Wrap labelText: qsTr("Transaction file location:") + translationManager.emptyString - text: walletManager.urlToLocalPath(saveTxDialog.fileUrl) + text: walletManager.urlToLocalPath(saveTxDialog.selectedFile) fontSize: 16 } @@ -161,7 +163,7 @@ Rectangle { width: 200 KeyNavigation.tab: doneButton onClicked: { - oshelper.openContainingFolder(walletManager.urlToLocalPath(saveTxDialog.fileUrl)) + oshelper.openContainingFolder(walletManager.urlToLocalPath(saveTxDialog.selectedFile)) } } diff --git a/components/TextBlock.qml b/components/TextBlock.qml index 9d1d0cf43f..9c263b8139 100644 --- a/components/TextBlock.qml +++ b/components/TextBlock.qml @@ -1,4 +1,4 @@ -import QtQuick 2.9 +import QtQuick import "../components" as MoneroComponents diff --git a/components/TextPlain.qml b/components/TextPlain.qml index ed6b7977bf..252f2481b9 100644 --- a/components/TextPlain.qml +++ b/components/TextPlain.qml @@ -1,4 +1,4 @@ -import QtQuick 2.9 +import QtQuick import "." as MoneroComponents import "effects/" as MoneroEffects diff --git a/components/TextPlainArea.qml b/components/TextPlainArea.qml index 648662b274..f972510302 100644 --- a/components/TextPlainArea.qml +++ b/components/TextPlainArea.qml @@ -1,5 +1,5 @@ -import QtQuick 2.9 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Controls import "." as MoneroComponents diff --git a/components/TipItem.qml b/components/TipItem.qml index a6223b488f..f26136e57b 100644 --- a/components/TipItem.qml +++ b/components/TipItem.qml @@ -26,8 +26,8 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Window 2.1 +import QtQuick +import QtQuick.Window import "../components" as MoneroComponents diff --git a/components/TitleBar.qml b/components/TitleBar.qml index 978f72c27a..a9110e32e0 100644 --- a/components/TitleBar.qml +++ b/components/TitleBar.qml @@ -26,12 +26,13 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Window 2.0 -import QtGraphicalEffects 1.0 -import QtQuick.Layouts 1.2 +import QtQuick +import QtQuick.Effects +import QtQuick.Window +import QtQuick.Layouts + +import FontAwesome -import FontAwesome 1.0 import "." as MoneroComponents import "effects/" as MoneroEffects @@ -281,17 +282,17 @@ Rectangle { source: MoneroComponents.Style.titleBarLogoSource visible: { - if(!isOpenGL) return true; + if(GraphicsInfo.api === GraphicsInfo.Software) return true; if(!MoneroComponents.Style.blackTheme) return true; return false; } } - Colorize { - visible: isOpenGL && MoneroComponents.Style.blackTheme + MultiEffect { + visible: GraphicsInfo.api !== GraphicsInfo.Software && MoneroComponents.Style.blackTheme anchors.fill: imgLogo source: imgLogo - saturation: 0.0 + saturation: -1.0 } } @@ -421,21 +422,9 @@ Rectangle { MouseArea { enabled: persistentSettings.customDecorations - property var previousPosition anchors.fill: parent propagateComposedEvents: true - onPressed: previousPosition = globalCursor.getPosition() + onPressed: appWindow.startSystemMove() onDoubleClicked: root.maximizeClicked() - onPositionChanged: { - if (pressedButtons == Qt.LeftButton) { - var pos = globalCursor.getPosition() - var dx = pos.x - previousPosition.x - var dy = pos.y - previousPosition.y - - appWindow.x += dx - appWindow.y += dy - previousPosition = pos - } - } } } diff --git a/components/Tooltip.qml b/components/Tooltip.qml index 8903cb536a..a50f44bdcc 100644 --- a/components/Tooltip.qml +++ b/components/Tooltip.qml @@ -26,11 +26,12 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import FontAwesome -import FontAwesome 1.0 import "." as MoneroComponents Rectangle { diff --git a/components/TxConfirmationDialog.qml b/components/TxConfirmationDialog.qml index 51da022f72..75b632cb6d 100644 --- a/components/TxConfirmationDialog.qml +++ b/components/TxConfirmationDialog.qml @@ -26,14 +26,14 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 1.4 as QtQuickControls1 -import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import FontAwesome import "../components" as MoneroComponents import "../js/Utils.js" as Utils -import FontAwesome 1.0 Rectangle { id: root @@ -189,7 +189,7 @@ Rectangle { Layout.fillWidth: true Layout.preferredHeight: 71 - QtQuickControls1.BusyIndicator { + BusyIndicator { id: txAmountBusyIndicator Layout.fillHeight: true Layout.fillWidth: true @@ -381,7 +381,7 @@ Rectangle { Layout.fillWidth: true Layout.preferredHeight: 50 - QtQuickControls1.BusyIndicator { + BusyIndicator { visible: !bottomTextAnimation.running running: !bottomTextAnimation.running scale: .5 diff --git a/components/UpdateDialog.qml b/components/UpdateDialog.qml index c3710cf7b4..c5386d322d 100644 --- a/components/UpdateDialog.qml +++ b/components/UpdateDialog.qml @@ -26,9 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts import moneroComponents.Downloader 1.0 diff --git a/components/WarningBox.qml b/components/WarningBox.qml index 62f6912a39..d88c31da44 100644 --- a/components/WarningBox.qml +++ b/components/WarningBox.qml @@ -1,6 +1,6 @@ -import QtQuick 2.9 -import QtQuick.Layouts 1.1 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import "." as MoneroComponents diff --git a/components/effects/ColorTransition.qml b/components/effects/ColorTransition.qml index ebafeb33fb..680c0df163 100644 --- a/components/effects/ColorTransition.qml +++ b/components/effects/ColorTransition.qml @@ -26,8 +26,7 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtGraphicalEffects 1.0 +import QtQuick import "../" as MoneroComponents diff --git a/components/effects/GradientBackground.qml b/components/effects/GradientBackground.qml index 608ce8158e..58758b1d97 100644 --- a/components/effects/GradientBackground.qml +++ b/components/effects/GradientBackground.qml @@ -26,8 +26,8 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtGraphicalEffects 1.0 +import QtQuick +import QtQuick.Shapes import "../" as MoneroComponents @@ -49,34 +49,49 @@ Item { // background software renderer Rectangle { - visible: !isOpenGL + visible: GraphicsInfo.api === GraphicsInfo.Software anchors.fill: parent color: root.fallBackColor } - // background opengl - LinearGradient { - visible: isOpenGL + // accelerated background + Shape { + id: gradientShape + visible: GraphicsInfo.api !== GraphicsInfo.Software anchors.fill: parent - start: root.start - end: root.end - gradient: Gradient { - GradientStop { - id: gradientStart - position: root.posStart - color: root.initialStartColor - } - GradientStop { - id: gradientStop - position: root.posStop - color: root.initialStopColor + + ShapePath { + strokeWidth: -1 + fillGradient: LinearGradient { + x1: root.start.x + y1: root.start.y + x2: root.end.x + y2: root.end.y + + GradientStop { + id: gradientStart + position: root.posStart + color: root.initialStartColor + } + GradientStop { + id: gradientStop + position: root.posStop + color: root.initialStopColor + } } + + startX: 0 + startY: 0 + PathLine { x: gradientShape.width; y: 0 } + PathLine { x: gradientShape.width; y: gradientShape.height } + PathLine { x: 0; y: gradientShape.height } + PathLine { x: 0; y: 0 } } states: [ State { name: "black"; - when: isOpenGL && MoneroComponents.Style.blackTheme + when: GraphicsInfo.api !== GraphicsInfo.Software && MoneroComponents.Style.blackTheme PropertyChanges { target: gradientStart color: root.blackColorStart @@ -87,7 +102,7 @@ Item { } }, State { name: "white"; - when: isOpenGL && !MoneroComponents.Style.blackTheme + when: GraphicsInfo.api !== GraphicsInfo.Software && !MoneroComponents.Style.blackTheme PropertyChanges { target: gradientStart color: root.whiteColorStart diff --git a/components/effects/ImageMask.qml b/components/effects/ImageMask.qml index 0ed8eff267..31dfbdd5dd 100644 --- a/components/effects/ImageMask.qml +++ b/components/effects/ImageMask.qml @@ -26,15 +26,16 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtGraphicalEffects 1.0 +import QtQuick +import QtQuick.Effects + +import FontAwesome import "../" as MoneroComponents -import FontAwesome 1.0 Item { // Use this component to color+opacity change images with transparency (svg/png) - // Does not work in low graphics mode, use fontAwesome fallback option. + // Does not work with the software renderer, use the FontAwesome fallback. id: root property string image: "" @@ -63,12 +64,13 @@ Item { visible: false } - ColorOverlay { + MultiEffect { id: imgMockColor anchors.fill: root source: svgMask - color: root.color - visible: image && isOpenGL + colorization: 1.0 + colorizationColor: root.color + visible: image && GraphicsInfo.api !== GraphicsInfo.Software } Text { diff --git a/fonts/FontAwesome/FontAwesome.qml b/fonts/FontAwesome/FontAwesome.qml index f2cfbab15e..b1403fe611 100644 --- a/fonts/FontAwesome/FontAwesome.qml +++ b/fonts/FontAwesome/FontAwesome.qml @@ -1,76 +1,70 @@ pragma Singleton -import QtQuick 2.9 -Object { +import QtQuick +QtObject { //Font Awesome version 5.15.3 - FontLoader { - id: regular + readonly property FontLoader regular: FontLoader { source: "./fa-regular-400.otf" } - - FontLoader { - id: brands + readonly property FontLoader brands: FontLoader { source: "./fa-brands-400.otf" } - - FontLoader { - id: solid + readonly property FontLoader solid: FontLoader { source: "./fa-solid-900.otf" } - property string fontFamily: regular.name - property string fontFamilyBrands: brands.name - property string fontFamilySolid: solid.name + readonly property string fontFamily: regular.name + readonly property string fontFamilyBrands: brands.name + readonly property string fontFamilySolid: solid.name // Icons used in Monero GUI (Font Awesome version 5.15.3) // To add new icons, check unicodes in Font Awesome Free's Cheatsheet: // https://fontawesome.com/v5/cheatsheet/free/solid // https://fontawesome.com/v5/cheatsheet/free/regular // https://fontawesome.com/v5/cheatsheet/free/brands - - property string addressBook : "\uf2b9" - property string arrowCircleRight : "\uf0a9" - property string arrowDown : "\uf063" - property string arrowLeft : "\uf060" - property string arrowRight : "\uf061" - property string cashRegister: "\uf788" - property string checkCircle: "\uf058" - property string clipboard : "\uf0ea" - property string clockO : "\uf017" - property string cloud : "\uf0c2" - property string desktop : "\uf108" - property string edit : "\uf044" - property string ellipsisH : "\uf141" - property string exclamationCircle : "\uf06a" - property string eye : "\uf06e" - property string eyeSlash : "\uf070" - property string folderOpen : "\uf07c" - property string globe : "\uf0ac" - property string home : "\uf015" - property string houseUser : "\ue065" - property string infinity : "\uf534" - property string info : "\uf129" - property string key : "\uf084" - property string language : "\uf1ab" - property string lock : "\uf023" - property string magnifyingGlass : "\uf002" - property string minus : "\uf068" - property string minusCircle : "\uf056" - property string moonO : "\uf186" - property string monero : "\uf3d0" - property string paste : "\uf0ea" - property string pencilSquare : "\uf14b" - property string plus : "\uf067" - property string plusCircle : "\uf055" - property string productHunt : "\uf288" - property string qrcode : "\uf029" - property string questionCircle : "\uf059" - property string random : "\uf074" - property string repeat : "\uf01e" - property string searchPlus : "\uf00e" - property string server : "\uf233" - property string shieldAlt : "\uf3ed" - property string signOutAlt : "\uf2f5" - property string times : "\uf00d" + readonly property string addressBook: "\uf2b9" + readonly property string arrowCircleRight: "\uf0a9" + readonly property string arrowDown: "\uf063" + readonly property string arrowLeft: "\uf060" + readonly property string arrowRight: "\uf061" + readonly property string cashRegister: "\uf788" + readonly property string checkCircle: "\uf058" + readonly property string clipboard: "\uf0ea" + readonly property string clockO: "\uf017" + readonly property string cloud: "\uf0c2" + readonly property string desktop: "\uf108" + readonly property string edit: "\uf044" + readonly property string ellipsisH: "\uf141" + readonly property string exclamationCircle: "\uf06a" + readonly property string eye: "\uf06e" + readonly property string eyeSlash: "\uf070" + readonly property string folderOpen: "\uf07c" + readonly property string globe: "\uf0ac" + readonly property string home: "\uf015" + readonly property string houseUser: "\ue065" + readonly property string infinity: "\uf534" + readonly property string info: "\uf129" + readonly property string key: "\uf084" + readonly property string language: "\uf1ab" + readonly property string lock: "\uf023" + readonly property string magnifyingGlass: "\uf002" + readonly property string minus: "\uf068" + readonly property string minusCircle: "\uf056" + readonly property string moonO: "\uf186" + readonly property string monero: "\uf3d0" + readonly property string paste: "\uf0ea" + readonly property string pencilSquare: "\uf14b" + readonly property string plus: "\uf067" + readonly property string plusCircle: "\uf055" + readonly property string productHunt: "\uf288" + readonly property string qrcode: "\uf029" + readonly property string questionCircle: "\uf059" + readonly property string random: "\uf074" + readonly property string repeat: "\uf01e" + readonly property string searchPlus: "\uf00e" + readonly property string server: "\uf233" + readonly property string shieldAlt: "\uf3ed" + readonly property string signOutAlt: "\uf2f5" + readonly property string times: "\uf00d" } diff --git a/fonts/FontAwesome/Object.qml b/fonts/FontAwesome/Object.qml deleted file mode 100644 index 8e4738d5a0..0000000000 --- a/fonts/FontAwesome/Object.qml +++ /dev/null @@ -1,8 +0,0 @@ -import QtQuick 2.9 - -QtObject { - id: object - default property alias children: object.__children - - property list __children: [QtObject {}] -} diff --git a/images/right.svg b/images/right.svg index 3087e689f2..5c7f95b250 100644 --- a/images/right.svg +++ b/images/right.svg @@ -1,3 +1,3 @@ - + diff --git a/main.qml b/main.qml index f4a7843d3c..429c3e868a 100644 --- a/main.qml +++ b/main.qml @@ -26,15 +26,16 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQml.Models 2.2 -import QtQuick 2.9 -import QtQuick.Window 2.0 -import QtQuick.Controls 1.1 -import QtQuick.Controls.Styles 1.1 -import QtQuick.Dialogs 1.2 -import QtGraphicalEffects 1.0 +import QtQml.Models +import QtCore +import QtQuick +import QtQuick.Effects +import QtQuick.Window +import QtQuick.Controls +import QtQuick.Dialogs -import FontAwesome 1.0 + +import FontAwesome import moneroComponents.Network 1.0 import moneroComponents.Wallet 1.0 @@ -68,7 +69,7 @@ ApplicationWindow { property bool hideBalanceForced: false property bool ctrlPressed: false property alias persistentSettings : persistentSettings - property string accountsDir: !persistentSettings.portable ? moneroAccountsDir : persistentSettings.portableFolderName + "/wallets" + property string accountsDir: !portableSettings.portable ? moneroAccountsDir : portableSettings.portableFolderName + "/wallets" property var currentWallet; property bool disconnected: currentWallet ? currentWallet.disconnected : false property var transaction; @@ -836,7 +837,6 @@ ApplicationWindow { middlePanel.advancedView.miningView.update() informationPopup.text += qsTr("\n\nExiting p2pool. Please check that port 18083 is available.") + translationManager.emptyString; } - informationPopup.icon = StandardIcon.Critical informationPopup.onCloseCallback = null informationPopup.open(); } @@ -976,8 +976,8 @@ ApplicationWindow { FileDialog { id: saveTxDialog title: "Please choose a location" - folder: "file://" + appWindow.accountsDir - selectExisting: false; + currentFolder: "file://" + appWindow.accountsDir + fileMode: FileDialog.SaveFile onAccepted: { handleTransactionConfirmed() @@ -1021,12 +1021,12 @@ ApplicationWindow { // View only wallet - we save the tx if(viewOnly){ // No file specified - abort - if(!saveTxDialog.fileUrl) { + if(!saveTxDialog.selectedFile) { currentWallet.disposeTransaction(transaction) return; } - var path = walletManager.urlToLocalPath(saveTxDialog.fileUrl) + var path = walletManager.urlToLocalPath(saveTxDialog.selectedFile) // Store to file transaction.setFilename(path); @@ -1041,7 +1041,6 @@ ApplicationWindow { console.log("Error committing transaction: " + transaction.errorString); informationPopup.title = qsTr("Error") + translationManager.emptyString informationPopup.text = qsTr("Couldn't send the money: ") + transaction.errorString - informationPopup.icon = StandardIcon.Critical informationPopup.onCloseCallback = null; informationPopup.open(); } else { @@ -1098,10 +1097,8 @@ ApplicationWindow { if (result.indexOf("error|") === 0) { var errorString = result.split("|")[1]; informationPopup.text = qsTr("Couldn't generate a proof because of the following reason: \n") + errorString + translationManager.emptyString; - informationPopup.icon = StandardIcon.Critical; } else { informationPopup.text = result; - informationPopup.icon = StandardIcon.Critical; } } @@ -1132,10 +1129,8 @@ ApplicationWindow { var confirmations = results[4]; informationPopup.title = qsTr("Payment proof check") + translationManager.emptyString; - informationPopup.icon = StandardIcon.Information if (!good) { informationPopup.text = qsTr("Bad signature"); - informationPopup.icon = StandardIcon.Critical; } else if (received > 0) { if (in_pool) { informationPopup.text = qsTr("This address received %1 monero, but the transaction is not yet mined").arg(walletManager.displayAmount(received)); @@ -1151,19 +1146,16 @@ ApplicationWindow { else if (results.length == 2 && results[0] === "true") { var good = results[1] === "true"; informationPopup.title = qsTr("Payment proof check") + translationManager.emptyString; - informationPopup.icon = good ? StandardIcon.Information : StandardIcon.Critical; informationPopup.text = good ? qsTr("Good signature") : qsTr("Bad signature"); } else if (isReserveProof && results[0] === "true") { var good = results[1] === "true"; informationPopup.title = qsTr("Reserve proof check") + translationManager.emptyString; - informationPopup.icon = good ? StandardIcon.Information : StandardIcon.Critical; informationPopup.text = good ? qsTr("Good signature on %1 total and %2 spent.").arg(results[2]).arg(results[3]) : qsTr("Bad signature"); } else { informationPopup.title = qsTr("Error") + translationManager.emptyString; informationPopup.text = currentWallet.errorString; - informationPopup.icon = StandardIcon.Critical } informationPopup.onCloseCallback = null informationPopup.open() @@ -1363,7 +1355,7 @@ ApplicationWindow { function fiatApiUpdateBalance(balance){ // update balance card var bFiat = "?.??" - if (!hideBalanceForced && !persistentSettings.hideBalance) { + if (!hideBalanceForced && !persistentSettings.hideBalance && appWindow.fiatPrice > 0) { bFiat = fiatApiConvertToFiat(balance); } leftPanel.balanceFiatString = bFiat; @@ -1374,6 +1366,12 @@ ApplicationWindow { } Component.onCompleted: { + logger.resetLogFilePath(portableSettings.portable); + if (persistentSettings.logLevel == 5) + walletManager.setLogCategories(persistentSettings.logCategories) + else + walletManager.setLogLevel(persistentSettings.logLevel) + if (screenAvailableWidth > width) { x = (screenAvailableWidth - width) / 2; } @@ -1403,16 +1401,14 @@ ApplicationWindow { mainApp.closing.connect(appWindow.close); if( appWindow.qrScannerEnabled ){ - console.log("qrScannerEnabled : load component QRCodeScanner"); var component = Qt.createComponent("components/QRCodeScanner.qml"); if (component.status == Component.Ready) { - console.log("Camera component ready"); cameraUi = component.createObject(appWindow); } else { - console.log("component not READY !!!"); + console.error("QR scanner component not ready:", component.errorString()); appWindow.qrScannerEnabled = false; } - } else console.log("qrScannerEnabled disabled"); + } if(!walletsFound()) { wizard.wizardState = "wizardLanguage"; @@ -1420,12 +1416,11 @@ ApplicationWindow { } else { wizard.wizardState = "wizardHome"; rootItem.state = "normal" - logger.resetLogFilePath(persistentSettings.portable); openWallet("wizard"); } const desktopEntryEnabled = (typeof builtWithDesktopEntry != "undefined") && builtWithDesktopEntry; - if (persistentSettings.askDesktopShortcut && !persistentSettings.portable && desktopEntryEnabled) { + if (persistentSettings.askDesktopShortcut && !portableSettings.portable && desktopEntryEnabled) { persistentSettings.askDesktopShortcut = false; if (isTails) { @@ -1433,7 +1428,6 @@ ApplicationWindow { } else if (isLinux) { confirmationDialog.title = qsTr("Desktop entry") + translationManager.emptyString; confirmationDialog.text = qsTr("Would you like to register Monero GUI Desktop entry?") + translationManager.emptyString; - confirmationDialog.icon = StandardIcon.Question; confirmationDialog.cancelText = qsTr("No") + translationManager.emptyString; confirmationDialog.okText = qsTr("Yes") + translationManager.emptyString; confirmationDialog.onAcceptedCallback = function() { @@ -1447,13 +1441,18 @@ ApplicationWindow { remoteNodesModel.initialize(); } - MoneroSettings { - id: persistentSettings - fileName: { + PortableSettings { + id: portableSettings + unportableFileName: { if(isTails && tailsUsePersistence) return homePath + "/Persistent/Monero/monero-core.conf"; return ""; } + } + + Settings { + id: persistentSettings + location: portableSettings.location property bool askDesktopShortcut: isLinux property bool askStopLocalNode: true @@ -1718,18 +1717,17 @@ ApplicationWindow { } // Choose blockchain folder - FileDialog { + FolderDialog { id: blockchainFileDialog property string directory: "" signal changed(); title: "Please choose a folder" - selectFolder: true - folder: "file://" + persistentSettings.blockchainDataDir + currentFolder: "file://" + persistentSettings.blockchainDataDir onRejected: console.log("data dir selection canceled") onAccepted: { - var dataDir = walletManager.urlToLocalPath(blockchainFileDialog.fileUrl) + var dataDir = walletManager.urlToLocalPath(blockchainFileDialog.selectedFolder) var validator = daemonManager.validateDataDir(dataDir, estimatedBlockchainSize); if(validator.valid) { persistentSettings.blockchainDataDir = dataDir; @@ -1745,8 +1743,6 @@ ApplicationWindow { if(!validator.lmdbExists) confirmationDialog.text += qsTr("Note: lmdb folder not found. A new folder will be created.") + "\n\n" - confirmationDialog.icon = StandardIcon.Question - // Continue confirmationDialog.onAcceptedCallback = function() { persistentSettings.blockchainDataDir = dataDir @@ -1757,7 +1753,7 @@ ApplicationWindow { confirmationDialog.open() } - blockchainFileDialog.directory = blockchainFileDialog.fileUrl; + blockchainFileDialog.directory = blockchainFileDialog.selectedFolder; delete validator; } } @@ -1782,11 +1778,9 @@ ApplicationWindow { appWindow.walletPassword = passwordDialog.password; informationPopup.title = qsTr("Information") + translationManager.emptyString; informationPopup.text = qsTr("Password changed successfully") + translationManager.emptyString; - informationPopup.icon = StandardIcon.Information; } else { informationPopup.title = qsTr("Error") + translationManager.emptyString; informationPopup.text = qsTr("Error: ") + currentWallet.errorString; - informationPopup.icon = StandardIcon.Critical; } informationPopup.onCloseCallback = null; informationPopup.open(); @@ -1942,11 +1936,14 @@ ApplicationWindow { } } - FastBlur { + MultiEffect { id: blur anchors.fill: blurredArea source: blurredArea - radius: 64 + blurEnabled: true + blurMax: 64 + blur: 1.0 + autoPaddingEnabled: false visible: passwordDialog.visible || inputDialog.visible || splash.visible || updateDialog.visible || devicePassphraseDialog.visible || txConfirmationPopup.visible || successfulTxPopup.visible || remoteNodeDialog.visible @@ -2188,7 +2185,6 @@ ApplicationWindow { // Show confirmation dialog confirmationDialog.title = qsTr("Local node is running") + translationManager.emptyString; confirmationDialog.text = qsTr("Do you want to stop local node or keep it running in the background?") + translationManager.emptyString; - confirmationDialog.icon = StandardIcon.Question; confirmationDialog.cancelText = qsTr("Force stop") + translationManager.emptyString; confirmationDialog.okText = qsTr("Keep it running") + translationManager.emptyString; confirmationDialog.onAcceptedCallback = function() { @@ -2200,7 +2196,7 @@ ApplicationWindow { confirmationDialog.open(); } - onClosing: { + onClosing: function(close) { close.accepted = false; console.log("blocking close event"); if(isAndroid) { @@ -2398,7 +2394,7 @@ ApplicationWindow { anchors.fill: parent anchors.topMargin: titleBar.height color: MoneroComponents.Style.blackTheme ? "black" : "white" - opacity: isOpenGL ? 0.3 : inputDialog.visible || splash.visible ? 0.7 : 1.0 + opacity: GraphicsInfo.api !== GraphicsInfo.Software ? 0.3 : inputDialog.visible || splash.visible ? 0.7 : 1.0 MoneroEffects.ColorTransition { targetObj: parent diff --git a/pages/Account.qml b/pages/Account.qml index 9bf0fe9c3d..779a00b247 100644 --- a/pages/Account.qml +++ b/pages/Account.qml @@ -26,12 +26,14 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 2.0 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.2 -import FontAwesome 1.0 +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs + +import FontAwesome import "../components" as MoneroComponents import "../components/effects/" as MoneroEffects @@ -47,9 +49,8 @@ Rectangle { id: pageAccount color: "transparent" property var model - property alias accountHeight: mainLayout.height - property alias balanceAllText: balanceAll.text - property alias unlockedBalanceAllText: unlockedBalanceAll.text + property string balanceAllText + property string unlockedBalanceAllText property bool selectAndSend: false property int currentAccountIndex @@ -65,17 +66,38 @@ Rectangle { Clipboard { id: clipboard } - /* main layout */ - ColumnLayout { - id: mainLayout + ListView { + id: subaddressAccountListView anchors.margins: 20 anchors.topMargin: 40 + anchors.fill: parent + clip: true + boundsBehavior: ListView.StopAtBounds + reuseItems: true + cacheBuffer: 100 + currentIndex: currentAccountIndex + headerPositioning: ListView.InlineHeader + + ScrollBar.vertical: ScrollBar { + id: subaddressAccountScrollBar + policy: ScrollBar.AsNeeded + parent: pageAccount + anchors.top: parent.top + anchors.topMargin: 40 + anchors.right: parent.right + anchors.rightMargin: 6 + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 + active: !isMac || subaddressAccountListView.moving || hovered || pressed + z: 2 + palette.mid: "#8E8E93" + palette.dark: "#B8B8BD" + } - anchors.left: parent.left - anchors.top: parent.top - anchors.right: parent.right - - spacing: 20 + header: ColumnLayout { + id: mainLayout + width: subaddressAccountListView.width + spacing: 20 ColumnLayout { id: balanceRow @@ -103,6 +125,7 @@ Rectangle { MoneroComponents.TextPlain { id: balanceAll + text: pageAccount.balanceAllText Layout.rightMargin: 87 font.family: MoneroComponents.Style.fontMonoRegular.name; font.pixelSize: 16 @@ -138,6 +161,7 @@ Rectangle { MoneroComponents.TextPlain { id: unlockedBalanceAll + text: pageAccount.unlockedBalanceAllText Layout.rightMargin: 87 font.family: MoneroComponents.Style.fontMonoRegular.name; font.pixelSize: 16 @@ -162,12 +186,13 @@ Rectangle { ColumnLayout { id: addressRow + Layout.fillWidth: true spacing: 0 RowLayout { spacing: 0 - MoneroComponents.LabelSubheader { + MoneroComponents.Label { Layout.fillWidth: true fontSize: 24 textFormat: Text.RichText @@ -191,46 +216,34 @@ Rectangle { inputDialog.open() } - Rectangle { - anchors.top: createNewAccountButton.bottom - anchors.topMargin: 8 - anchors.left: createNewAccountButton.left - anchors.right: createNewAccountButton.right - height: 2 - color: MoneroComponents.Style.appWindowBorderColor - - MoneroEffects.ColorTransition { - targetObj: parent - blackColor: MoneroComponents.Style._b_appWindowBorderColor - whiteColor: MoneroComponents.Style._w_appWindowBorderColor - } - } } } - ColumnLayout { - id: subaddressAccountListRow - property int subaddressAccountListItemHeight: 50 - Layout.topMargin: 6 + Rectangle { Layout.fillWidth: true - Layout.minimumWidth: 240 - Layout.preferredHeight: subaddressAccountListItemHeight * subaddressAccountListView.count - visible: subaddressAccountListView.count >= 1 + Layout.topMargin: 8 + Layout.preferredHeight: 2 + color: MoneroComponents.Style.appWindowBorderColor - ListView { - id: subaddressAccountListView - Layout.fillWidth: true - Layout.fillHeight: true - clip: true - boundsBehavior: ListView.StopAtBounds - interactive: false - currentIndex: currentAccountIndex + MoneroEffects.ColorTransition { + targetObj: parent + blackColor: MoneroComponents.Style._b_appWindowBorderColor + whiteColor: MoneroComponents.Style._w_appWindowBorderColor + } + } - delegate: Rectangle { + } + + } + + delegate: Rectangle { id: tableItem2 - height: subaddressAccountListRow.subaddressAccountListItemHeight - width: parent ? parent.width : undefined - Layout.fillWidth: true + required property int index + required property string address + required property string balance + required property string label + height: 50 + width: subaddressAccountListView.width color: itemMouseArea.containsMouse || index === currentAccountIndex ? MoneroComponents.Style.titleBarButtonHoverColor : "transparent" Rectangle { @@ -295,7 +308,7 @@ Rectangle { anchors.leftMargin: -addressLabel.width - 30 fontSize: 16 fontFamily: MoneroComponents.Style.fontMonoRegular.name; - text: TxUtils.addressTruncatePretty(address, mainLayout.width < 740 ? 1 : (mainLayout.width < 900 ? 2 : 3)) + text: TxUtils.addressTruncatePretty(address, subaddressAccountListView.width < 740 ? 1 : (subaddressAccountListView.width < 900 ? 2 : 3)) themeTransition: false } @@ -339,7 +352,7 @@ Rectangle { fontAwesomeFallbackIcon: FontAwesome.edit fontAwesomeFallbackSize: 22 color: MoneroComponents.Style.defaultFontColor - opacity: isOpenGL ? 0.5 : 1 + opacity: GraphicsInfo.api !== GraphicsInfo.Software ? 0.5 : 1 fontAwesomeFallbackOpacity: 0.5 Layout.preferredWidth: 23 Layout.preferredHeight: 21 @@ -354,7 +367,7 @@ Rectangle { fontAwesomeFallbackIcon: FontAwesome.clipboard fontAwesomeFallbackSize: 22 color: MoneroComponents.Style.defaultFontColor - opacity: isOpenGL ? 0.5 : 1 + opacity: GraphicsInfo.api !== GraphicsInfo.Software ? 0.5 : 1 fontAwesomeFallbackOpacity: 0.5 Layout.preferredWidth: 16 Layout.preferredHeight: 21 @@ -369,22 +382,19 @@ Rectangle { } } - onCurrentIndexChanged: { - appWindow.onWalletUpdate(); - } - } - } + onCurrentIndexChanged: { + appWindow.onWalletUpdate(); + } - Rectangle { - color: MoneroComponents.Style.appWindowBorderColor - Layout.fillWidth: true - height: 1 + footer: Rectangle { + width: subaddressAccountListView.width + height: 1 + color: MoneroComponents.Style.appWindowBorderColor - MoneroEffects.ColorTransition { - targetObj: parent - blackColor: MoneroComponents.Style._b_appWindowBorderColor - whiteColor: MoneroComponents.Style._w_appWindowBorderColor - } + MoneroEffects.ColorTransition { + targetObj: parent + blackColor: MoneroComponents.Style._b_appWindowBorderColor + whiteColor: MoneroComponents.Style._w_appWindowBorderColor } } } @@ -396,8 +406,8 @@ Rectangle { subaddressAccountListView.model = appWindow.currentWallet.subaddressAccountModel; appWindow.currentWallet.subaddress.refresh(appWindow.currentWallet.currentSubaddressAccount) - balanceAll.text = walletManager.displayAmount(appWindow.currentWallet.balanceAll()) + " XMR" - unlockedBalanceAll.text = walletManager.displayAmount(appWindow.currentWallet.unlockedBalanceAll()) + " XMR" + balanceAllText = walletManager.displayAmount(appWindow.currentWallet.balanceAll()) + " XMR" + unlockedBalanceAllText = walletManager.displayAmount(appWindow.currentWallet.unlockedBalanceAll()) + " XMR" } } diff --git a/pages/AddressBook.qml b/pages/AddressBook.qml index 65e315e269..5406ca9997 100644 --- a/pages/AddressBook.qml +++ b/pages/AddressBook.qml @@ -26,10 +26,12 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 2.0 -import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.2 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs + +import FontAwesome import "../components" as MoneroComponents import "../components/effects/" as MoneroEffects @@ -39,7 +41,6 @@ import moneroComponents.AddressBook 1.0 import moneroComponents.AddressBookModel 1.0 import moneroComponents.Clipboard 1.0 import moneroComponents.NetworkType 1.0 -import FontAwesome 1.0 Rectangle { id: root @@ -223,7 +224,7 @@ Rectangle { id: sendToButton image: "qrc:///images/arrow-right-in-circle-outline-medium-white.svg" color: MoneroComponents.Style.defaultFontColor - opacity: isOpenGL ? 0.5 : 1 + opacity: GraphicsInfo.api !== GraphicsInfo.Software ? 0.5 : 1 fontAwesomeFallbackIcon: FontAwesome.arrowRight fontAwesomeFallbackSize: 22 fontAwesomeFallbackOpacity: 0.5 @@ -252,7 +253,7 @@ Rectangle { id: editEntryButton image: "qrc:///images/edit.svg" color: MoneroComponents.Style.defaultFontColor - opacity: isOpenGL ? 0.5 : 1 + opacity: GraphicsInfo.api !== GraphicsInfo.Software ? 0.5 : 1 fontAwesomeFallbackIcon: FontAwesome.edit fontAwesomeFallbackSize: 22 fontAwesomeFallbackOpacity: 0.5 @@ -270,7 +271,7 @@ Rectangle { id: copyButton image: "qrc:///images/copy.svg" color: MoneroComponents.Style.defaultFontColor - opacity: isOpenGL ? 0.5 : 1 + opacity: GraphicsInfo.api !== GraphicsInfo.Software ? 0.5 : 1 fontAwesomeFallbackIcon: FontAwesome.clipboard fontAwesomeFallbackSize: 22 fontAwesomeFallbackOpacity: 0.5 @@ -539,7 +540,6 @@ Rectangle { function oa_message(text) { oaPopup.title = qsTr("OpenAlias error") + translationManager.emptyString oaPopup.text = text - oaPopup.icon = StandardIcon.Information oaPopup.onCloseCallback = null oaPopup.open() } @@ -550,7 +550,6 @@ Rectangle { qsTr("OpenAlias: ") + openAlias + "\n\n" + qsTr("Resolved address: ") + resolvedAddress + "\n\n" + qsTr("Only use this address if you trust this OpenAlias result.") + translationManager.emptyString - confirmationDialog.icon = StandardIcon.Question confirmationDialog.cancelText = qsTr("Cancel") + translationManager.emptyString confirmationDialog.okText = qsTr("Use address") + translationManager.emptyString confirmationDialog.onAcceptedCallback = onAccepted diff --git a/pages/Advanced.qml b/pages/Advanced.qml index 5cb0d3d8b7..3db983c642 100644 --- a/pages/Advanced.qml +++ b/pages/Advanced.qml @@ -26,10 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts import "../components" as MoneroComponents import "." @@ -94,7 +93,13 @@ ColumnLayout { } previousView = currentView if (currentView) { - stackView.replace(currentView) + if (stackView.currentItem !== currentView) { + if (stackView.depth > 0) { + stackView.replace(currentView) + } else { + stackView.push(currentView) + } + } if (typeof currentView.onPageCompleted === "function") { currentView.onPageCompleted(); } @@ -127,26 +132,6 @@ ColumnLayout { anchors.fill: parent clip: false // otherwise animation will affect left panel - delegate: StackViewDelegate { - pushTransition: StackViewTransition { - PropertyAnimation { - target: enterItem - property: "x" - from: (navbarId.currentIndex < navbarId.previousIndex ? 1 : -1) * - target.width - to: 0 - duration: 300 - easing.type: Easing.OutCubic - } - PropertyAnimation { - target: exitItem - property: "x" - from: 0 - to: (navbarId.currentIndex < navbarId.previousIndex ? 1 : -1) * target.width - duration: 300 - easing.type: Easing.OutCubic - } - } - } } } diff --git a/pages/History.qml b/pages/History.qml index 6871405d4c..e8aab8091c 100644 --- a/pages/History.qml +++ b/pages/History.qml @@ -26,17 +26,17 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.0 -import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.2 -import QtGraphicalEffects 1.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Dialogs +import FontAwesome + import moneroComponents.Wallet 1.0 import moneroComponents.WalletManager 1.0 import moneroComponents.TransactionHistory 1.0 import moneroComponents.TransactionInfo 1.0 import moneroComponents.TransactionHistoryModel 1.0 import moneroComponents.Clipboard 1.0 -import FontAwesome 1.0 import "../components/effects/" as MoneroEffects import "../components" as MoneroComponents @@ -1560,7 +1560,8 @@ Rectangle { var destinations = _model.data(idx, TransactionHistoryModel.TransactionDestinationsRole); var time = _model.data(idx, TransactionHistoryModel.TransactionTimeRole); var date = _model.data(idx, TransactionHistoryModel.TransactionDateRole); - var blockheight = _model.data(idx, TransactionHistoryModel.TransactionBlockHeightRole); + var blockheightValue = _model.data(idx, TransactionHistoryModel.TransactionBlockHeightRole); + var blockheight = blockheightValue ? blockheightValue.toString() : ""; var confirmations = _model.data(idx, TransactionHistoryModel.TransactionConfirmationsRole); var confirmationsRequired = _model.data(idx, TransactionHistoryModel.TransactionConfirmationsRequiredRole); var fee = _model.data(idx, TransactionHistoryModel.TransactionFeeRole); @@ -1753,15 +1754,14 @@ Rectangle { + translationManager.emptyString; } - FileDialog { + FolderDialog { id: writeCSVFileDialog title: qsTr("Please choose a folder") + translationManager.emptyString - selectFolder: true onRejected: { console.log("csv write canceled") } onAccepted: { - var dataDir = walletManager.urlToLocalPath(writeCSVFileDialog.fileUrl); + var dataDir = walletManager.urlToLocalPath(writeCSVFileDialog.selectedFolder); var written = currentWallet.history.writeCSV(currentWallet.currentSubaddressAccount, dataDir); if(written !== ""){ @@ -1769,7 +1769,6 @@ Rectangle { var text = qsTr("CSV file written to: %1").arg(written) + "\n\n" text += qsTr("Tip: Use your favorite spreadsheet software to sort on blockheight.") + "\n\n" + translationManager.emptyString; confirmationDialog.text = text; - confirmationDialog.icon = StandardIcon.Information; confirmationDialog.cancelText = qsTr("Open folder") + translationManager.emptyString; confirmationDialog.onAcceptedCallback = null; confirmationDialog.onRejectedCallback = function() { @@ -1779,7 +1778,6 @@ Rectangle { } else { informationPopup.title = qsTr("Error") + translationManager.emptyString; informationPopup.text = qsTr("Error exporting transaction data.") + "\n\n" + translationManager.emptyString; - informationPopup.icon = StandardIcon.Critical; informationPopup.onCloseCallback = null; informationPopup.open(); @@ -1792,7 +1790,7 @@ Rectangle { } catch(err) {} finally { - writeCSVFileDialog.folder = _folder; + writeCSVFileDialog.currentFolder = _folder; } } } diff --git a/pages/Keys.qml b/pages/Keys.qml index 99618d901b..71a8c9a493 100644 --- a/pages/Keys.qml +++ b/pages/Keys.qml @@ -26,15 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.2 +import QtQuick +import QtQuick.Layouts import moneroComponents.Clipboard 1.0 -import "../version.js" as Version import "../components" as MoneroComponents -import "." 1.0 Rectangle { diff --git a/pages/Mining.qml b/pages/Mining.qml index 85bd4d0a39..f8313439d0 100644 --- a/pages/Mining.qml +++ b/pages/Mining.qml @@ -26,10 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQml.Models 2.2 -import QtQuick 2.9 -import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.2 +import QtQml.Models +import QtQuick +import QtQuick.Layouts +import QtQuick.Dialogs import "../components" as MoneroComponents import moneroComponents.Wallet 1.0 import moneroComponents.P2PoolManager 1.0 @@ -305,7 +305,6 @@ Rectangle { else { confirmationDialog.title = qsTr("P2Pool installation") + translationManager.emptyString; confirmationDialog.text = qsTr("P2Pool will be installed at %1. Proceed?").arg(applicationDirectory) + translationManager.emptyString; - confirmationDialog.icon = StandardIcon.Question; confirmationDialog.cancelText = qsTr("No") + translationManager.emptyString; confirmationDialog.okText = qsTr("Yes") + translationManager.emptyString; confirmationDialog.onAcceptedCallback = function() { @@ -586,7 +585,6 @@ Rectangle { errorPopup.text = message if (persistentSettings.useRemoteNode && !persistentSettings.allowRemoteNodeMining) errorPopup.text += qsTr("Mining is only available on local daemons. Run a local daemon to be able to mine.
") + translationManager.emptyString - errorPopup.icon = StandardIcon.Critical errorPopup.open() } @@ -681,7 +679,6 @@ allArgs = allArgs.filter( ( el ) => !defaultArgs.includes( el.split(" ")[0] ) ) default: errorPopup.text = qsTr("Unknown error.") + translationManager.emptyString; } - errorPopup.icon = StandardIcon.Critical errorPopup.open() update() } @@ -690,7 +687,6 @@ allArgs = allArgs.filter( ( el ) => !defaultArgs.includes( el.split(" ")[0] ) ) statusMessage.visible = false informationPopup.title = qsTr("P2Pool Installation Succeeded") + translationManager.emptyString; informationPopup.text = qsTr("P2Pool has successfully installed."); - informationPopup.icon = StandardIcon.Critical informationPopup.open() update() } diff --git a/pages/Receive.qml b/pages/Receive.qml index 27071010bc..60d58e89f3 100644 --- a/pages/Receive.qml +++ b/pages/Receive.qml @@ -26,12 +26,14 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 2.0 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.2 -import FontAwesome 1.0 +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs + +import FontAwesome import "../components" as MoneroComponents import "../components/effects/" as MoneroEffects @@ -49,8 +51,8 @@ Rectangle { id: pageReceive color: "transparent" property var model - property alias receiveHeight: mainLayout.height property var state: "Address" + property string selectedAddressDescription: "(" + qsTr("no label") + ")" + translationManager.emptyString function renameSubaddressLabel(_index){ inputDialog.labelText = qsTr("Set the label of the selected address:") + translationManager.emptyString; @@ -62,46 +64,82 @@ Rectangle { } function updateSelectedAddressDisplay() { + if (!appWindow.currentWallet || subaddressListView.currentIndex < 0) { + return; + } + appWindow.current_subaddress_table_index = subaddressListView.currentIndex; appWindow.current_address = appWindow.currentWallet.address( appWindow.currentWallet.currentSubaddressAccount, subaddressListView.currentIndex ); if (subaddressListView.currentIndex == 0) { - selectedAddressDrescription.text = qsTr("Primary address") + translationManager.emptyString; + selectedAddressDescription = qsTr("Primary address") + translationManager.emptyString; } else { var selectedAddressLabel = appWindow.currentWallet.getSubaddressLabel(appWindow.currentWallet.currentSubaddressAccount, appWindow.current_subaddress_table_index); if (selectedAddressLabel == "") { - selectedAddressDrescription.text = "(" + qsTr("no label") + ")" + translationManager.emptyString + selectedAddressDescription = "(" + qsTr("no label") + ")" + translationManager.emptyString } else { - selectedAddressDrescription.text = selectedAddressLabel + selectedAddressDescription = selectedAddressLabel } } } function generateQRCodeString() { - if (pageReceive.state == "PaymentRequest") { - return walletManager.make_uri(appWindow.current_address, - amountToReceiveXMR.text, - txDescriptionInput.text, receiverNameInput.text); - } else { - return walletManager.make_uri(appWindow.current_address); - } + return subaddressListView.headerItem + ? subaddressListView.headerItem.generateQRCodeString() + : walletManager.make_uri(appWindow.current_address); } Clipboard { id: clipboard } - /* main layout */ - ColumnLayout { - id: mainLayout + ListView { + id: subaddressListView anchors.margins: 20 anchors.topMargin: 40 + anchors.fill: parent + clip: true + boundsBehavior: ListView.StopAtBounds + reuseItems: true + cacheBuffer: 100 + headerPositioning: ListView.InlineHeader + + ScrollBar.vertical: ScrollBar { + id: subaddressScrollBar + policy: ScrollBar.AsNeeded + parent: pageReceive + anchors.top: parent.top + anchors.topMargin: 40 + anchors.right: parent.right + anchors.rightMargin: 6 + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 + active: !isMac || subaddressListView.moving || hovered || pressed + z: 2 + palette.mid: "#8E8E93" + palette.dark: "#B8B8BD" + } + + header: ColumnLayout { + id: mainLayout + width: subaddressListView.width + spacing: 15 - anchors.left: parent.left - anchors.top: parent.top - anchors.right: parent.right + function clearFields() { + amountToReceiveFiat.text = ""; + amountToReceiveXMR.text = ""; + txDescriptionInput.text = ""; + receiverNameInput.text = ""; + } - spacing: 15 + function generateQRCodeString() { + if (pageReceive.state == "PaymentRequest") { + return walletManager.make_uri(appWindow.current_address, + amountToReceiveXMR.text, + txDescriptionInput.text, receiverNameInput.text); + } + return walletManager.make_uri(appWindow.current_address); + } ColumnLayout { id: selectedAddressDetailsColumn @@ -114,16 +152,16 @@ Rectangle { Layout.bottomMargin: 10 MoneroComponents.NavbarItem { - active: state == "Address" + active: pageReceive.state == "Address" text: qsTr("Address") + translationManager.emptyString - onSelected: state = "Address" + onSelected: pageReceive.state = "Address" } MoneroComponents.NavbarItem { - active: state == "PaymentRequest" + active: pageReceive.state == "PaymentRequest" text: qsTr("Payment request") + translationManager.emptyString onSelected: { - state = "PaymentRequest"; + pageReceive.state = "PaymentRequest"; qrCodeTextMouseArea.hoverEnabled = true; } } @@ -144,7 +182,7 @@ Rectangle { anchors.margins: 1 smooth: false fillMode: Image.PreserveAspectFit - source: "image://qrcode/" + generateQRCodeString(); + source: "image://qrcode/" + mainLayout.generateQRCodeString(); MouseArea { anchors.fill: parent @@ -153,7 +191,7 @@ Rectangle { acceptedButtons: Qt.LeftButton | Qt.RightButton onEntered: qrCodeTooltip.tooltipPopup.open() onExited: qrCodeTooltip.tooltipPopup.close() - onClicked: { + onClicked: (mouse) => { if (mouse.button == Qt.LeftButton){ walletManager.saveQrCodeToClipboard(generateQRCodeString()); appWindow.showStatusMessage(qsTr("QR code copied to clipboard") + translationManager.emptyString, 3); @@ -279,8 +317,8 @@ Rectangle { amountToReceiveXMR.text = fiatApiConvertToXMR(amountToReceiveFiat.text); } } - validator: RegExpValidator { - regExp: /^\s*(\d{1,8})?([\.,]\d{1,2})?\s*$/ + validator: RegularExpressionValidator { + regularExpression: /^\s*(\d{1,8})?([\.,]\d{1,2})?\s*$/ } } @@ -339,8 +377,8 @@ Rectangle { amountToReceiveFiat.text = fiatApiConvertToFiat(amountToReceiveXMR.text); } } - validator: RegExpValidator { - regExp: /^\s*(\d{1,8})?([\.,]\d{1,12})?\s*$/ + validator: RegularExpressionValidator { + regularExpression: /^\s*(\d{1,8})?([\.,]\d{1,12})?\s*$/ } } @@ -454,7 +492,7 @@ Rectangle { Layout.topMargin: 10 visible: pageReceive.state == "Address" horizontalAlignment: Text.AlignHCenter - text: "(" + qsTr("no label") + ")" + translationManager.emptyString + text: pageReceive.selectedAddressDescription wrapMode: Text.WordWrap font.family: MoneroComponents.Style.fontRegular.name font.pixelSize: 17 @@ -524,12 +562,13 @@ Rectangle { ColumnLayout { id: addressRow + Layout.fillWidth: true spacing: 0 RowLayout { spacing: 0 - MoneroComponents.LabelSubheader { + MoneroComponents.Label { Layout.fillWidth: true fontSize: 24 textFormat: Text.RichText @@ -552,45 +591,33 @@ Rectangle { inputDialog.open() } - Rectangle { - anchors.top: createAddressButton.bottom - anchors.topMargin: 8 - anchors.left: createAddressButton.left - anchors.right: createAddressButton.right - height: 2 - color: MoneroComponents.Style.appWindowBorderColor - - MoneroEffects.ColorTransition { - targetObj: parent - blackColor: MoneroComponents.Style._b_appWindowBorderColor - whiteColor: MoneroComponents.Style._w_appWindowBorderColor - } - } } } - ColumnLayout { - id: subaddressListRow - property int subaddressListItemHeight: 50 - Layout.topMargin: 6 + Rectangle { Layout.fillWidth: true - Layout.minimumWidth: 240 - Layout.preferredHeight: subaddressListItemHeight * subaddressListView.count - visible: subaddressListView.count >= 1 + Layout.topMargin: 8 + Layout.preferredHeight: 2 + color: MoneroComponents.Style.appWindowBorderColor - ListView { - id: subaddressListView - Layout.fillWidth: true - Layout.fillHeight: true - clip: true - boundsBehavior: ListView.StopAtBounds - interactive: false + MoneroEffects.ColorTransition { + targetObj: parent + blackColor: MoneroComponents.Style._b_appWindowBorderColor + whiteColor: MoneroComponents.Style._w_appWindowBorderColor + } + } - delegate: Rectangle { + } + + } + + delegate: Rectangle { id: tableItem2 - height: subaddressListRow.subaddressListItemHeight - width: parent ? parent.width : undefined - Layout.fillWidth: true + required property int index + required property string address + required property string label + height: 50 + width: subaddressListView.width color: itemMouseArea.containsMouse || index === appWindow.current_subaddress_table_index ? MoneroComponents.Style.titleBarButtonHoverColor : "transparent" Rectangle { @@ -656,7 +683,7 @@ Rectangle { anchors.leftMargin: -addressLabel.width - 5 fontSize: 16 fontFamily: MoneroComponents.Style.fontMonoRegular.name; - text: TxUtils.addressTruncatePretty(address, mainLayout.width < 520 ? 1 : (mainLayout.width < 650 ? 2 : 3)) + text: TxUtils.addressTruncatePretty(address, subaddressListView.width < 520 ? 1 : (subaddressListView.width < 650 ? 2 : 3)) themeTransition: false } @@ -694,7 +721,7 @@ Rectangle { fontAwesomeFallbackIcon: FontAwesome.edit fontAwesomeFallbackSize: 22 color: MoneroComponents.Style.defaultFontColor - opacity: isOpenGL ? 0.5 : 1 + opacity: GraphicsInfo.api !== GraphicsInfo.Software ? 0.5 : 1 fontAwesomeFallbackOpacity: 0.5 Layout.preferredWidth: 23 Layout.preferredHeight: 21 @@ -712,7 +739,7 @@ Rectangle { fontAwesomeFallbackIcon: FontAwesome.clipboard fontAwesomeFallbackSize: 22 color: MoneroComponents.Style.defaultFontColor - opacity: isOpenGL ? 0.5 : 1 + opacity: GraphicsInfo.api !== GraphicsInfo.Software ? 0.5 : 1 fontAwesomeFallbackOpacity: 0.5 Layout.preferredWidth: 16 Layout.preferredHeight: 21 @@ -726,54 +753,50 @@ Rectangle { } } } - onCurrentItemChanged: updateSelectedAddressDisplay() - } - } - Rectangle { - color: MoneroComponents.Style.appWindowBorderColor - Layout.fillWidth: true - height: 1 + onCurrentItemChanged: updateSelectedAddressDisplay() - MoneroEffects.ColorTransition { - targetObj: parent - blackColor: MoneroComponents.Style._b_appWindowBorderColor - whiteColor: MoneroComponents.Style._w_appWindowBorderColor - } + footer: Rectangle { + width: subaddressListView.width + height: 1 + color: MoneroComponents.Style.appWindowBorderColor + + MoneroEffects.ColorTransition { + targetObj: parent + blackColor: MoneroComponents.Style._b_appWindowBorderColor + whiteColor: MoneroComponents.Style._w_appWindowBorderColor } } + } - MessageDialog { + MessageDialog { id: receivePageDialog - standardButtons: StandardButton.Ok + buttons: MessageDialog.Ok } - FileDialog { + FileDialog { id: qrFileDialog title: qsTr("Please choose a name") + translationManager.emptyString - folder: shortcuts.pictures - selectExisting: false + fileMode: FileDialog.SaveFile nameFilters: ["Image (*.png)"] onAccepted: { - if(!walletManager.saveQrCode(generateQRCodeString(), walletManager.urlToLocalPath(fileUrl))) { - console.log("Failed to save QrCode to file " + walletManager.urlToLocalPath(fileUrl) ) + if(!walletManager.saveQrCode(generateQRCodeString(), walletManager.urlToLocalPath(selectedFile))) { + console.log("Failed to save QrCode to file " + walletManager.urlToLocalPath(selectedFile) ) receivePageDialog.title = qsTr("Save QrCode") + translationManager.emptyString; - receivePageDialog.text = qsTr("Failed to save QrCode to ") + walletManager.urlToLocalPath(fileUrl) + translationManager.emptyString; - receivePageDialog.icon = StandardIcon.Error + receivePageDialog.text = qsTr("Failed to save QrCode to ") + walletManager.urlToLocalPath(selectedFile) + translationManager.emptyString; receivePageDialog.open() } else { - appWindow.showStatusMessage(qsTr("QR code saved to ") + walletManager.urlToLocalPath(fileUrl) + translationManager.emptyString, 3); + appWindow.showStatusMessage(qsTr("QR code saved to ") + walletManager.urlToLocalPath(selectedFile) + translationManager.emptyString, 3); } } - } } function onPageCompleted() { console.log("Receive page loaded"); pageReceive.clearFields(); - subaddressListView.model = appWindow.currentWallet.subaddressModel; if (appWindow.currentWallet) { + subaddressListView.model = appWindow.currentWallet.subaddressModel; appWindow.currentWallet.subaddress.refresh(appWindow.currentWallet.currentSubaddressAccount) var numSubaddresses = appWindow.currentWallet.numSubaddresses(appWindow.currentWallet.currentSubaddressAccount); if (subaddressListView.currentIndex == -1 || subaddressListView.currentIndex >= numSubaddresses) { @@ -784,10 +807,9 @@ Rectangle { } function clearFields() { - amountToReceiveFiat.text = ""; - amountToReceiveXMR.text = ""; - txDescriptionInput.text = ""; - receiverNameInput.text = ""; + if (subaddressListView.headerItem) { + subaddressListView.headerItem.clearFields(); + } } function onPageClosed() { diff --git a/pages/SharedRingDB.qml b/pages/SharedRingDB.qml index cfe1e2533d..b65850c760 100644 --- a/pages/SharedRingDB.qml +++ b/pages/SharedRingDB.qml @@ -26,11 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.2 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs import "../components" as MoneroComponents import moneroComponents.Clipboard 1.0 @@ -81,7 +80,7 @@ Rectangle { MessageDialog { id: sharedRingDBDialog - standardButtons: StandardButton.Ok + buttons: MessageDialog.Ok } MoneroComponents.Label { diff --git a/pages/Sign.qml b/pages/Sign.qml index 7fcc121944..0bd38ffb48 100644 --- a/pages/Sign.qml +++ b/pages/Sign.qml @@ -26,11 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.2 +import QtQuick +import QtQuick.Layouts +import QtQuick.Dialogs import moneroComponents.Clipboard 1.0 import moneroComponents.WalletManager 1.0 @@ -49,7 +47,7 @@ Rectangle { // dynamically change onclose handler property var onCloseCallback id: signatureVerificationMessage - standardButtons: StandardButton.Ok + buttons: MessageDialog.Ok onAccepted: { if (onCloseCallback) { onCloseCallback() @@ -61,12 +59,10 @@ Rectangle { if (result) { signatureVerificationMessage.title = qsTr("Good signature") + translationManager.emptyString signatureVerificationMessage.text = qsTr("This is a good signature") + translationManager.emptyString - signatureVerificationMessage.icon = StandardIcon.Information } else { signatureVerificationMessage.title = qsTr("Bad signature") + translationManager.emptyString signatureVerificationMessage.text = qsTr("This signature did not verify") + translationManager.emptyString - signatureVerificationMessage.icon = StandardIcon.Critical } signatureVerificationMessage.open() } @@ -401,22 +397,22 @@ Rectangle { FileDialog { id: signFileDialog title: qsTr("Please choose a file to sign") + translationManager.emptyString; - folder: "file://" + currentFolder: "file://" nameFilters: [ "*"] onAccepted: { - signFileLine.text = walletManager.urlToLocalPath(signFileDialog.fileUrl) + signFileLine.text = walletManager.urlToLocalPath(signFileDialog.selectedFile) } } FileDialog { id: verifyFileDialog title: qsTr("Please choose a file to verify") + translationManager.emptyString; - folder: "file://" + currentFolder: "file://" nameFilters: [ "*"] onAccepted: { - verifyFileLine.text = walletManager.urlToLocalPath(verifyFileDialog.fileUrl) + verifyFileLine.text = walletManager.urlToLocalPath(verifyFileDialog.selectedFile) } } } diff --git a/pages/Transfer.qml b/pages/Transfer.qml index a201a4e81a..45ccb5d7dc 100644 --- a/pages/Transfer.qml +++ b/pages/Transfer.qml @@ -26,16 +26,17 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQml.Models 2.2 -import QtQuick 2.9 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.2 +import QtQml.Models +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs +import FontAwesome + import moneroComponents.Clipboard 1.0 import moneroComponents.PendingTransaction 1.0 import moneroComponents.Wallet 1.0 import moneroComponents.NetworkType 1.0 -import FontAwesome 1.0 import "../components" import "../components" as MoneroComponents import "." 1.0 @@ -100,7 +101,6 @@ Rectangle { function oa_message(text) { oaPopup.title = qsTr("OpenAlias error") + translationManager.emptyString oaPopup.text = text - oaPopup.icon = StandardIcon.Information oaPopup.onCloseCallback = null oaPopup.open() } @@ -111,7 +111,6 @@ Rectangle { qsTr("OpenAlias: ") + openAlias + "\n\n" + qsTr("Resolved address: ") + resolvedAddress + "\n\n" + qsTr("Only use this address if you trust this OpenAlias result.") + translationManager.emptyString - confirmationDialog.icon = StandardIcon.Question confirmationDialog.cancelText = qsTr("Cancel") + translationManager.emptyString confirmationDialog.okText = qsTr("Use address") + translationManager.emptyString confirmationDialog.onAcceptedCallback = onAccepted @@ -520,8 +519,8 @@ Rectangle { amount = text; } - validator: RegExpValidator { - regExp: /^\s*(\d{1,8})?([\.,]\d{1,12})?\s*$/ + validator: RegularExpressionValidator { + regularExpression: /^\s*(\d{1,8})?([\.,]\d{1,12})?\s*$/ } } @@ -1022,11 +1021,11 @@ Rectangle { FileDialog { id: signTxDialog title: qsTr("Please choose a file") + translationManager.emptyString - folder: "file://" + appWindow.accountsDir + currentFolder: "file://" + appWindow.accountsDir nameFilters: [ "Unsigned transfers (*)"] onAccepted: { - var path = walletManager.urlToLocalPath(fileUrl); + var path = walletManager.urlToLocalPath(selectedFile); // Load the unsigned tx from file var transaction = currentWallet.loadTxFile(path); @@ -1034,7 +1033,6 @@ Rectangle { console.error("Can't load unsigned transaction: ", transaction.errorString); informationPopup.title = qsTr("Error") + translationManager.emptyString; informationPopup.text = qsTr("Can't load unsigned transaction: ") + transaction.errorString - informationPopup.icon = StandardIcon.Critical informationPopup.onCloseCallback = null informationPopup.open(); // deleting transaction object, we don't want memleaks @@ -1045,7 +1043,6 @@ Rectangle { // Show confirmation dialog confirmationDialog.title = qsTr("Confirmation") + translationManager.emptyString - confirmationDialog.icon = StandardIcon.Question confirmationDialog.onAcceptedCallback = function() { transaction.sign(path+"_signed"); transaction.destroy(); @@ -1066,20 +1063,18 @@ Rectangle { FileDialog { id: submitTxDialog title: qsTr("Please choose a file") + translationManager.emptyString - folder: "file://" + appWindow.accountsDir + currentFolder: "file://" + appWindow.accountsDir nameFilters: [ "signed transfers (*)"] onAccepted: { - if(!currentWallet.submitTxFile(walletManager.urlToLocalPath(fileUrl))){ + if(!currentWallet.submitTxFile(walletManager.urlToLocalPath(selectedFile))){ informationPopup.title = qsTr("Error") + translationManager.emptyString; informationPopup.text = qsTr("Can't submit transaction: ") + currentWallet.errorString - informationPopup.icon = StandardIcon.Critical informationPopup.onCloseCallback = null informationPopup.open(); } else { informationPopup.title = qsTr("Information") + translationManager.emptyString informationPopup.text = qsTr("Monero sent successfully") + translationManager.emptyString - informationPopup.icon = StandardIcon.Information informationPopup.onCloseCallback = null informationPopup.open(); } @@ -1092,11 +1087,10 @@ Rectangle { FileDialog { id: exportOutputsDialog - selectMultiple: false - selectExisting: false + fileMode: FileDialog.SaveFile onAccepted: { - console.log(walletManager.urlToLocalPath(exportOutputsDialog.fileUrl)) - if (currentWallet.exportOutputs(walletManager.urlToLocalPath(exportOutputsDialog.fileUrl), true)) { + console.log(walletManager.urlToLocalPath(exportOutputsDialog.selectedFile)) + if (currentWallet.exportOutputs(walletManager.urlToLocalPath(exportOutputsDialog.selectedFile), true)) { appWindow.showStatusMessage(qsTr("Outputs successfully exported to file") + translationManager.emptyString, 3); } else { appWindow.showStatusMessage(currentWallet.errorString, 5); @@ -1109,12 +1103,10 @@ Rectangle { FileDialog { id: importOutputsDialog - selectMultiple: false - selectExisting: true title: qsTr("Please choose a file") + translationManager.emptyString onAccepted: { - console.log(walletManager.urlToLocalPath(importOutputsDialog.fileUrl)) - if (currentWallet.importOutputs(walletManager.urlToLocalPath(importOutputsDialog.fileUrl))) { + console.log(walletManager.urlToLocalPath(importOutputsDialog.selectedFile)) + if (currentWallet.importOutputs(walletManager.urlToLocalPath(importOutputsDialog.selectedFile))) { appWindow.showStatusMessage(qsTr("Outputs successfully imported to wallet") + translationManager.emptyString, 3); } else { appWindow.showStatusMessage(currentWallet.errorString, 5); @@ -1128,11 +1120,10 @@ Rectangle { //ExportKeyImagesDialog FileDialog { id: exportKeyImagesDialog - selectMultiple: false - selectExisting: false + fileMode: FileDialog.SaveFile onAccepted: { - console.log(walletManager.urlToLocalPath(exportKeyImagesDialog.fileUrl)) - if (currentWallet.exportKeyImages(walletManager.urlToLocalPath(exportKeyImagesDialog.fileUrl), true)) { + console.log(walletManager.urlToLocalPath(exportKeyImagesDialog.selectedFile)) + if (currentWallet.exportKeyImages(walletManager.urlToLocalPath(exportKeyImagesDialog.selectedFile), true)) { appWindow.showStatusMessage(qsTr("Key images successfully exported to file") + translationManager.emptyString, 3); } else { appWindow.showStatusMessage(currentWallet.errorString, 5); @@ -1146,12 +1137,10 @@ Rectangle { //ImportKeyImagesDialog FileDialog { id: importKeyImagesDialog - selectMultiple: false - selectExisting: true title: qsTr("Please choose a file") + translationManager.emptyString onAccepted: { - console.log(walletManager.urlToLocalPath(importKeyImagesDialog.fileUrl)) - if (currentWallet.importKeyImages(walletManager.urlToLocalPath(importKeyImagesDialog.fileUrl))) { + console.log(walletManager.urlToLocalPath(importKeyImagesDialog.selectedFile)) + if (currentWallet.importKeyImages(walletManager.urlToLocalPath(importKeyImagesDialog.selectedFile))) { appWindow.showStatusMessage(qsTr("Key images successfully imported to wallet") + translationManager.emptyString, 3); } else { appWindow.showStatusMessage(currentWallet.errorString, 5); diff --git a/pages/TxKey.qml b/pages/TxKey.qml index e2808e2aaf..b2a9cd23a5 100644 --- a/pages/TxKey.qml +++ b/pages/TxKey.qml @@ -26,10 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.1 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts import "../components" as MoneroComponents import moneroComponents.Clipboard 1.0 @@ -125,8 +124,8 @@ Rectangle { } error = walletManager.amountFromString(text) > appWindow.getUnlockedBalance(); } - validator: RegExpValidator { - regExp: /^\s*(\d{1,8})?([\.,]\d{1,12})?\s*$/ + validator: RegularExpressionValidator { + regularExpression: /^\s*(\d{1,8})?([\.,]\d{1,12})?\s*$/ } } diff --git a/pages/merchant/Merchant.qml b/pages/merchant/Merchant.qml index da4d68fda4..70e892e0f5 100644 --- a/pages/merchant/Merchant.qml +++ b/pages/merchant/Merchant.qml @@ -26,12 +26,11 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.1 -import QtQuick.Controls 2.0 -import QtGraphicalEffects 1.0 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Dialogs 1.2 +import QtQuick +import QtQuick.Effects +import QtQuick.Layouts +import QtQuick.Controls +import QtQuick.Dialogs import moneroComponents.Clipboard 1.0 import moneroComponents.Wallet 1.0 @@ -206,16 +205,15 @@ Item { } } - DropShadow { + MultiEffect { anchors.fill: source - cached: true - horizontalOffset: 3 - verticalOffset: 3 - radius: 8.0 - samples: 16 - color: "#20000000" - smooth: true source: tracker + shadowEnabled: true + shadowHorizontalOffset: 3 + shadowVerticalOffset: 3 + shadowBlur: 1.0 + blurMax: 8 + shadowColor: "#20000000" } Rectangle { @@ -244,7 +242,7 @@ Item { MouseArea { anchors.fill: parent acceptedButtons: Qt.RightButton - onClicked: { + onClicked: (mouse) => { if (mouse.button == Qt.RightButton){ qrMenu.x = this.mouseX; qrMenu.y = this.mouseY; @@ -266,16 +264,15 @@ Item { } } - DropShadow { + MultiEffect { anchors.fill: source - cached: true - horizontalOffset: 3 - verticalOffset: 3 - radius: 8.0 - samples: 16 - color: "#30000000" - smooth: true source: qrImg + shadowEnabled: true + shadowHorizontalOffset: 3 + shadowVerticalOffset: 3 + shadowBlur: 1.0 + blurMax: 8 + shadowColor: "#30000000" } } @@ -398,7 +395,6 @@ Item { // onClicked: { // merchantPageDialog.title = qsTr("Payment URL") + translationManager.emptyString; // merchantPageDialog.text = qsTr("payment url explanation") -// merchantPageDialog.icon = StandardIcon.Information // merchantPageDialog.open() // } // } @@ -451,16 +447,15 @@ Item { } } - DropShadow { + MultiEffect { anchors.fill: source - cached: true - horizontalOffset: 3 - verticalOffset: 3 - radius: 8.0 - samples: 16 - color: "#20000000" - smooth: true source: payment_url_container + shadowEnabled: true + shadowHorizontalOffset: 3 + shadowVerticalOffset: 3 + shadowBlur: 1.0 + blurMax: 8 + shadowColor: "#20000000" } Item { @@ -513,8 +508,8 @@ Item { amountToReceive.text = '0' + amountToReceive.text; } } - validator: RegExpValidator { - regExp: /^(\d{1,8})?([\.]\d{1,12})?$/ + validator: RegularExpressionValidator { + regularExpression: /^(\d{1,8})?([\.]\d{1,12})?$/ } } } @@ -653,7 +648,7 @@ Item { var confirmations = 0; var displayAmount = model.data(idx, TransactionHistoryModel.TransactionDisplayAmountRole); - if (blockHeight === undefined) { + if (!blockHeight) { in_txpool = true; } else { confirmations = model.data(idx, TransactionHistoryModel.TransactionConfirmationsRole); @@ -703,22 +698,20 @@ Item { MessageDialog { id: merchantPageDialog - standardButtons: StandardButton.Ok + buttons: MessageDialog.Ok } FileDialog { id: qrFileDialog title: "Please choose a name" - folder: shortcuts.pictures - selectExisting: false + fileMode: FileDialog.SaveFile nameFilters: ["Image (*.png)"] onAccepted: { - if (!walletManager.saveQrCode(walletManager.make_uri(appWindow.current_address, amountToReceive.text), walletManager.urlToLocalPath(fileUrl))) { - console.log("Failed to save QrCode to file " + walletManager.urlToLocalPath(fileUrl) ) - receivePageDialog.title = qsTr("Save QrCode") + translationManager.emptyString; - receivePageDialog.text = qsTr("Failed to save QrCode to ") + walletManager.urlToLocalPath(fileUrl) + translationManager.emptyString; - receivePageDialog.icon = StandardIcon.Error - receivePageDialog.open() + if (!walletManager.saveQrCode(walletManager.make_uri(appWindow.current_address, amountToReceive.text), walletManager.urlToLocalPath(selectedFile))) { + console.log("Failed to save QrCode to file " + walletManager.urlToLocalPath(selectedFile) ) + merchantPageDialog.title = qsTr("Save QrCode") + translationManager.emptyString; + merchantPageDialog.text = qsTr("Failed to save QrCode to ") + walletManager.urlToLocalPath(selectedFile) + translationManager.emptyString; + merchantPageDialog.open() } } } diff --git a/pages/merchant/MerchantCheckbox.qml b/pages/merchant/MerchantCheckbox.qml index bed9dea11b..e8b41ed3d6 100644 --- a/pages/merchant/MerchantCheckbox.qml +++ b/pages/merchant/MerchantCheckbox.qml @@ -26,9 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.1 -import QtGraphicalEffects 1.0 +import QtQuick +import QtQuick.Effects +import QtQuick.Layouts import "../../components" as MoneroComponents @@ -60,16 +60,15 @@ Item { source: "qrc:///images/uncheckedIcon.png" } } - DropShadow { + MultiEffect { anchors.fill: source - cached: true - horizontalOffset: 3 - verticalOffset: 3 - radius: 8.0 - samples: 16 - color: "#20000000" - smooth: true source: checkbox + shadowEnabled: true + shadowHorizontalOffset: 3 + shadowVerticalOffset: 3 + shadowBlur: 1.0 + blurMax: 8 + shadowColor: "#20000000" } } MoneroComponents.TextPlain { diff --git a/pages/merchant/MerchantTitlebar.qml b/pages/merchant/MerchantTitlebar.qml index 9d70b61bf8..47c554f43e 100644 --- a/pages/merchant/MerchantTitlebar.qml +++ b/pages/merchant/MerchantTitlebar.qml @@ -26,12 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Window 2.0 -import QtGraphicalEffects 1.0 -import QtQuick.Layouts 1.2 +import QtQuick +import QtQuick.Window +import QtQuick.Layouts -import FontAwesome 1.0 import "../../components/" as MoneroComponents import "../../components/effects/" as MoneroEffects @@ -178,20 +176,8 @@ Rectangle { MouseArea { enabled: persistentSettings.customDecorations - property var previousPosition anchors.fill: parent propagateComposedEvents: true - onPressed: previousPosition = globalCursor.getPosition() - onPositionChanged: { - if (pressedButtons == Qt.LeftButton) { - var pos = globalCursor.getPosition() - var dx = pos.x - previousPosition.x - var dy = pos.y - previousPosition.y - - appWindow.x += dx - appWindow.y += dy - previousPosition = pos - } - } + onPressed: appWindow.startSystemMove() } } diff --git a/pages/merchant/MerchantTrackingList.qml b/pages/merchant/MerchantTrackingList.qml index 611df81e09..0fb18828a7 100644 --- a/pages/merchant/MerchantTrackingList.qml +++ b/pages/merchant/MerchantTrackingList.qml @@ -26,11 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 2.0 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.2 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs import "../../js/Utils.js" as Utils import "../../components" as MoneroComponents diff --git a/pages/settings/Settings.qml b/pages/settings/Settings.qml index 17457f98f2..2c638cc1f7 100644 --- a/pages/settings/Settings.qml +++ b/pages/settings/Settings.qml @@ -26,11 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.2 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs import "../../js/Windows.js" as Windows import "../../js/Utils.js" as Utils import "../../components" as MoneroComponents @@ -105,7 +104,13 @@ ColumnLayout { } previousView = currentView if (currentView) { - stackView.replace(currentView) + if (stackView.currentItem !== currentView) { + if (stackView.depth > 0) { + stackView.replace(currentView) + } else { + stackView.push(currentView) + } + } if (typeof currentView.onPageCompleted === "function") { currentView.onPageCompleted(); } @@ -142,26 +147,6 @@ ColumnLayout { anchors.fill: parent clip: false // otherwise animation will affect left panel - delegate: StackViewDelegate { - pushTransition: StackViewTransition { - PropertyAnimation { - target: enterItem - property: "x" - from: (navbarId.currentIndex < navbarId.previousIndex ? 1 : -1) * - target.width - to: 0 - duration: 300 - easing.type: Easing.OutCubic - } - PropertyAnimation { - target: exitItem - property: "x" - from: 0 - to: (navbarId.currentIndex < navbarId.previousIndex ? 1 : -1) * target.width - duration: 300 - easing.type: Easing.OutCubic - } - } - } } } diff --git a/pages/settings/SettingsInfo.qml b/pages/settings/SettingsInfo.qml index e126383a62..f90f6fe6f7 100644 --- a/pages/settings/SettingsInfo.qml +++ b/pages/settings/SettingsInfo.qml @@ -26,10 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.1 -import QtQuick.Controls 2.0 -import QtQuick.Dialogs 1.2 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import QtQuick.Dialogs import "../../js/Wizard.js" as Wizard import "../../js/Utils.js" as Utils @@ -41,6 +41,16 @@ Rectangle { color: "transparent" Layout.fillWidth: true property alias infoHeight: infoLayout.height + readonly property string rendererName: ({ + [GraphicsInfo.OpenGL]: "OpenGL", + [GraphicsInfo.Vulkan]: "Vulkan", + [GraphicsInfo.Metal]: "Metal", + [GraphicsInfo.Direct3D11]: "Direct3D 11", + [GraphicsInfo.Direct3D12]: "Direct3D 12", + [GraphicsInfo.OpenVG]: "OpenVG", + [GraphicsInfo.Software]: "Software", + [GraphicsInfo.Null]: "Null" + })[GraphicsInfo.api] ?? "Unknown" property string walletModeString: { var modeStr; if(appWindow.walletMode === 0){ @@ -50,7 +60,7 @@ Rectangle { } else if(appWindow.walletMode === 2){ modeStr = "%1 (%2)".arg(qsTr("Advanced mode")).arg(persistentSettings.useRemoteNode ? qsTr("Remote node") : qsTr("Local node")) + translationManager.emptyString; } - return modeStr + (persistentSettings.portable ? ", %1".arg(qsTr("portable")) : ""); + return modeStr + (portableSettings.portable ? ", %1".arg(qsTr("portable")) : ""); } ColumnLayout { @@ -208,7 +218,6 @@ Rectangle { + "- Tx descriptions\n\n" + "The old wallet cache file will be renamed and can be restored later.\n" ); - confirmationDialog.icon = StandardIcon.Question confirmationDialog.onAcceptedCallback = function() { appWindow.closeWallet(function() { walletManager.clearWalletCache(persistentSettings.wallet_path); @@ -338,7 +347,7 @@ Rectangle { Layout.fillWidth: true color: MoneroComponents.Style.dimmedFontColor font.pixelSize: 14 - text: isOpenGL ? "OpenGL" : "Low graphics mode" + text: rendererName } Rectangle { @@ -395,7 +404,7 @@ Rectangle { data += "\nWallet log path: " + logger.logFilePath; data += "\nWallet mode: " + walletModeString; - data += "\nGraphics mode: " + (isOpenGL ? "OpenGL" : "Low graphics mode"); + data += "\nGraphics mode: " + rendererName; if (isTails) data += "\nTails: " + (tailsUsePersistence ? "persistent" : "persistence disabled"); diff --git a/pages/settings/SettingsLayout.qml b/pages/settings/SettingsLayout.qml index e73355ae88..8dcfd60956 100644 --- a/pages/settings/SettingsLayout.qml +++ b/pages/settings/SettingsLayout.qml @@ -26,10 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.1 -import QtQuick.Controls 2.0 -import QtQuick.Dialogs 1.2 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import QtQuick.Dialogs import moneroComponents.Wallet 1.0 diff --git a/pages/settings/SettingsLog.qml b/pages/settings/SettingsLog.qml index efe06f0e5c..5a47b25b6a 100644 --- a/pages/settings/SettingsLog.qml +++ b/pages/settings/SettingsLog.qml @@ -26,9 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.1 -import QtQuick.Controls 2.2 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import "../../js/Utils.js" as Utils import "../../components" as MoneroComponents @@ -175,6 +175,7 @@ Rectangle { font.pixelSize: 14 wrapMode: TextEdit.Wrap readOnly: true + background: null function logCommand(msg){ msg = log_color(msg, MoneroComponents.Style.blackTheme ? "lime" : "green"); consoleArea.append(msg); diff --git a/pages/settings/SettingsNode.qml b/pages/settings/SettingsNode.qml index 28480419ff..37f6da88a7 100644 --- a/pages/settings/SettingsNode.qml +++ b/pages/settings/SettingsNode.qml @@ -26,10 +26,11 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.1 -import QtQuick.Controls 2.0 -import FontAwesome 1.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls + +import FontAwesome import "../../components" as MoneroComponents import "../../components/effects" as MoneroEffects @@ -299,7 +300,7 @@ Rectangle{ onInputLabelLinkActivated: { //mouse.accepted = false if(persistentSettings.blockchainDataDir !== ""){ - blockchainFileDialog.folder = "file://" + persistentSettings.blockchainDataDir; + blockchainFileDialog.currentFolder = "file://" + persistentSettings.blockchainDataDir; } blockchainFileDialog.open(); blockchainFolder.focus = true; diff --git a/pages/settings/SettingsWallet.qml b/pages/settings/SettingsWallet.qml index f43d64e2ec..cd67a1c0c6 100644 --- a/pages/settings/SettingsWallet.qml +++ b/pages/settings/SettingsWallet.qml @@ -26,11 +26,12 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.1 -import QtQuick.Controls 2.0 -import QtQuick.Dialogs 1.2 -import FontAwesome 1.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import QtQuick.Dialogs + +import FontAwesome import "../../js/Utils.js" as Utils import "../../components" as MoneroComponents @@ -114,13 +115,11 @@ Rectangle { } else { informationPopup.text = qsTr("Error: ") + currentWallet.errorString; } - informationPopup.icon = StandardIcon.Critical informationPopup.onCloseCallback = null informationPopup.open(); } else { informationPopup.title = qsTr("Information") + translationManager.emptyString informationPopup.text = qsTr("Successfully rescanned spent outputs.") + translationManager.emptyString - informationPopup.icon = StandardIcon.Information informationPopup.onCloseCallback = null informationPopup.open(); } @@ -145,7 +144,6 @@ Rectangle { if (currentWallet.errorString == "The wallet has already seen 1 or more recent transactions than the scanned tx") { informationPopup.title = qsTr("Error") + translationManager.emptyString; informationPopup.text = qsTr("The wallet has already seen 1 or more recent transactions than the scanned transaction.\n\nIn order to rescan the transaction, you can re-sync your wallet by resetting the wallet restore height in the Settings > Info page. Make sure to use a restore height from before your wallet's earliest transaction.") + translationManager.emptyString; - informationPopup.icon = StandardIcon.Critical informationPopup.onCloseCallback = null informationPopup.open(); } else { diff --git a/qml.qrc b/qml.qrc deleted file mode 100644 index 9223423dc8..0000000000 --- a/qml.qrc +++ /dev/null @@ -1,304 +0,0 @@ - - - main.qml - LeftPanel.qml - MiddlePanel.qml - components/Dialog.qml - components/Label.qml - components/LanguageButton.qml - components/Navbar.qml - components/NavbarItem.qml - components/RemoteNodeDialog.qml - components/RemoteNodeList.qml - components/SettingsListItem.qml - components/Slider.qml - components/Tooltip.qml - components/UpdateDialog.qml - images/whatIsIcon.png - images/whatIsIcon@2x.png - components/MenuButton.qml - monero/utils/gpg_keys/binaryfate.asc - monero/utils/gpg_keys/fluffypony.asc - monero/utils/gpg_keys/luigi1111.asc - pages/Account.qml - pages/Advanced.qml - pages/Transfer.qml - pages/History.qml - pages/AddressBook.qml - pages/Mining.qml - components/ContextMenu.qml - components/ContextMenuItem.qml - components/NetworkStatusItem.qml - components/Input.qml - components/StandardButton.qml - components/LineEdit.qml - components/TipItem.qml - images/tip.png - components/MenuButtonDivider.qml - images/monero-vector.svg - components/StandardDropdown.qml - images/whiteDropIndicator.png - images/whiteDropIndicator@2x.png - components/CheckBox.qml - images/uncheckedIcon.png - images/uncheckedIcon@2x.png - components/DatePicker.qml - images/prevMonth.png - images/prevMonth@2x.png - components/TitleBar.qml - components/MenuBar.qml - images/resize.png - images/resize@2x.png - images/resizeHovered.png - images/resizeHovered@2x.png - images/nextPage.png - images/nextPage@2x.png - lang/languages.xml - lang/flags/bd.png - lang/flags/bg.png - lang/flags/br.png - lang/flags/catalonia.png - lang/flags/cn.png - lang/flags/hr.png - lang/flags/hu.png - lang/flags/cz.png - lang/flags/dk.png - lang/flags/eg.png - lang/flags/eo.png - lang/flags/fi.png - lang/flags/fr.png - lang/flags/de.png - lang/flags/in.png - lang/flags/id.png - lang/flags/il.png - lang/flags/ir.png - lang/flags/irl.png - lang/flags/it.png - lang/flags/jp.png - lang/flags/ku.png - lang/flags/lt.png - lang/flags/nl.png - lang/flags/pk.png - lang/flags/ps.png - lang/flags/pl.png - lang/flags/pt.png - lang/flags/ro.png - lang/flags/ru.png - lang/flags/rs.png - lang/flags/sk.png - lang/flags/si.png - lang/flags/za.png - lang/flags/kr.png - lang/flags/es.png - lang/flags/se.png - lang/flags/tw.png - lang/flags/tr.png - lang/flags/ua.png - lang/flags/gb.png - lang/flags/us.png - lang/flags/nb_NO.png - lang/flags/el.png - lang/flags/vi.png - lang/flags/is.png - pages/Receive.qml - pages/TxKey.qml - pages/SharedRingDB.qml - components/effects/ImageMask.qml - components/IconButton.qml - components/PasswordDialog.qml - components/InputDialog.qml - components/ProcessingSplash.qml - components/ProgressBar.qml - components/StandardDialog.qml - components/DevicePassphraseDialog.qml - pages/Sign.qml - components/DaemonManagerDialog.qml - version.js - components/QRCodeScanner.qml - components/TextBlock.qml - components/RemoteNodeEdit.qml - pages/Keys.qml - images/appicon.ico - images/card-background-black0.png - images/card-background-black1.png - images/card-background-black2.png - images/card-background-black3.png - images/card-background-black4.png - images/card-background-black5.png - images/card-background-black6.png - images/card-background-black7.png - images/card-background-black0@2x.png - images/card-background-black1@2x.png - images/card-background-black2@2x.png - images/card-background-black3@2x.png - images/card-background-black4@2x.png - images/card-background-black5@2x.png - images/card-background-black6@2x.png - images/card-background-black7@2x.png - images/card-background-white.png - images/card-background-white@2x.png - images/moneroLogo_white.png - images/titlebarLogo.png - images/titlebarLogo@2x.png - pages/merchant/MerchantTitlebar.qml - images/menuButtonGradient.png - fonts/Roboto-Medium.ttf - fonts/Roboto-Regular.ttf - fonts/Roboto-Light.ttf - fonts/Roboto-Bold.ttf - fonts/RobotoMono-Medium.ttf - fonts/RobotoMono-Regular.ttf - fonts/RobotoMono-Light.ttf - fonts/RobotoMono-Bold.ttf - components/Style.qml - components/qmldir - components/InlineButton.qml - images/lightning.png - images/lightning@2x.png - images/logout.png - images/logout@2x.png - images/moneroIcon-28x28.png - images/moneroIcon-28x28@2x.png - images/lightning-white.png - images/lightning-white@2x.png - components/InputMulti.qml - components/LineEditMulti.qml - components/LabelButton.qml - components/LabelSubheader.qml - images/arrow-right-medium-white.png - images/arrow-right-medium-white@2x.png - images/rightArrow.png - images/rightArrow@2x.png - images/historyBorderRadius.png - components/CheckBox2.qml - components/TextPlain.qml - components/TextPlainArea.qml - js/TxUtils.js - images/warning.png - images/warning@2x.png - js/Windows.js - js/Utils.js - components/RadioButton.qml - pages/settings/Settings.qml - pages/settings/SettingsWallet.qml - pages/settings/SettingsNode.qml - pages/settings/SettingsLog.qml - pages/settings/SettingsLayout.qml - pages/settings/SettingsInfo.qml - components/WarningBox.qml - images/miningxmr.png - images/miningxmr@2x.png - pages/merchant/Merchant.qml - pages/merchant/MerchantCheckbox.qml - pages/merchant/MerchantTrackingList.qml - images/merchant/arrow_right.png - images/merchant/bg.png - images/merchant/input_box.png - fonts/FontAwesome/fa-brands-400.otf - fonts/FontAwesome/fa-regular-400.otf - fonts/FontAwesome/fa-solid-900.otf - fonts/FontAwesome/FontAwesome.qml - fonts/FontAwesome/Object.qml - fonts/FontAwesome/qmldir - wizard/WizardAskPassword.qml - wizard/WizardController.qml - wizard/WizardCreateWallet1.qml - wizard/WizardCreateWallet2.qml - wizard/WizardCreateWallet3.qml - wizard/WizardCreateWallet4.qml - wizard/WizardCreateWallet5.qml - wizard/WizardCreateDevice1.qml - wizard/WizardDaemonSettings.qml - wizard/WizardHeader.qml - wizard/WizardHome.qml - wizard/WizardLanguage.qml - wizard/WizardNav.qml - wizard/WizardWalletInput.qml - wizard/WizardRestoreWallet1.qml - wizard/WizardRestoreWallet2.qml - wizard/WizardRestoreWallet3.qml - wizard/WizardRestoreWallet4.qml - wizard/WizardSummary.qml - wizard/WizardSummaryItem.qml - wizard/WizardModeSelection.qml - wizard/WizardModeRemoteNodeWarning.qml - wizard/WizardModeBootstrap.qml - wizard/WizardMenuItem.qml - js/Wizard.js - components/LanguageSidebar.qml - images/world-flags-globe.png - images/restore-wallet-from-hardware@2x.png - images/restore-wallet-from-hardware.png - images/open-wallet-from-file@2x.png - images/open-wallet-from-file.png - images/open-wallet-from-file-mainnet@2x.png - images/open-wallet-from-file-mainnet.png - images/open-wallet-from-file-stagenet@2x.png - images/open-wallet-from-file-stagenet.png - images/open-wallet-from-file-testnet@2x.png - images/open-wallet-from-file-testnet.png - images/open-wallet-from-file-view-only@2x.png - images/open-wallet-from-file-view-only.png - images/open-wallet-from-file-trezor@2x.png - images/open-wallet-from-file-trezor.png - images/restore-wallet@2x.png - images/restore-wallet.png - images/create-wallet@2x.png - images/create-wallet.png - images/remote-node.png - images/remote-node@2x.png - images/local-node.png - images/local-node@2x.png - images/local-node-full.png - images/local-node-full@2x.png - wizard/WizardOpenWallet1.qml - images/arrow-right-in-circle.png - images/arrow-right-in-circle@2x.png - images/right.svg - images/middlePanelShadow.png - images/themes/white/titlebarLogo@2x.png - images/themes/white/titlebarLogo.png - images/fullscreen.svg - images/close.svg - images/minimize.svg - images/themes/white/close.svg - images/themes/white/fullscreen.svg - images/themes/white/minimize.svg - components/effects/ColorTransition.qml - components/effects/GradientBackground.qml - images/check-white.svg - images/copy.svg - images/edit.svg - images/arrow-right-in-circle-outline-medium-white.svg - images/tails-grey.png - components/AdvancedOptionsItem.qml - images/busy-indicator.png - images/busy-indicator@2x.png - images/success.png - images/success@2x.png - components/SuccessfulTxDialog.qml - components/TxConfirmationDialog.qml - images/ledgerNanoS.png - images/ledgerNanoSPlus.png - images/ledgerNanoX.png - images/ledgerNanoGen5.png - images/ledgerStax.png - images/ledgerFlex.png - images/trezor3.png - images/trezor5.png - images/trezorT.png - images/trezorT@2x.png - qtquickcontrols2.conf - images/write-down.png - images/write-down-white.png - images/write-down@2x.png - images/write-down-white@2x.png - images/verify.png - images/verify-white.png - images/verify@2x.png - images/verify-white@2x.png - wizard/SeedListItem.qml - wizard/SeedListGrid.qml - wizard/template.pdf - - diff --git a/share/Info.plist b/share/Info.plist index 087ead8dc0..91fbc19ba3 100644 --- a/share/Info.plist +++ b/share/Info.plist @@ -8,6 +8,9 @@ NSPrincipalClass NSApplication + NSCameraUsageDescription + The camera is used to scan Monero QR codes. + CFBundleIconFile appicon.icns diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bcbda89cfe..e751d08f4e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,8 +2,6 @@ add_subdirectory(QR-Code-scanner) add_subdirectory(openpgp) add_subdirectory(zxcvbn-c) -qt5_add_resources(RESOURCES ../qml.qrc) - # Compile source files (.h/.cpp) file(GLOB SOURCE_FILES "*.h" @@ -50,36 +48,11 @@ if(APPLE) list(APPEND SOURCE_FILES "qt/macoshelper.mm") endif() -set(EXECUTABLE_FLAG) -if(MINGW) - set(EXECUTABLE_FLAG WIN32) - - set(ICON ${PROJECT_SOURCE_DIR}/images/appicon.ico) - set(ICON_RC ${CMAKE_CURRENT_BINARY_DIR}/icon.rc) - set(ICON_RES ${CMAKE_CURRENT_BINARY_DIR}/icon.o) - file(WRITE ${ICON_RC} "IDI_ICON1 ICON DISCARDABLE \"${ICON}\"") - find_program(Qt5_WINDRES_EXECUTABLE NAMES windres x86_64-w64-mingw32-windres REQUIRED CMAKE_FIND_ROOT_PATH_BOTH) - add_custom_command(OUTPUT ${ICON_RES} COMMAND ${Qt5_WINDRES_EXECUTABLE} ${ICON_RC} ${ICON_RES} MAIN_DEPENDENCY ${ICON_RC}) - list(APPEND RESOURCES ${ICON_RES}) -endif() - -if(APPLE) - set(ICON ${PROJECT_SOURCE_DIR}/images/appicon.icns) - set_source_files_properties(${ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") - list(APPEND RESOURCES ${ICON}) -endif() - set(monero_wallet_gui_sources ${SOURCE_FILES} - ${RESOURCES} ) -if(NOT ANDROID) - add_executable(monero-wallet-gui ${EXECUTABLE_FLAG} ${monero_wallet_gui_sources}) -else() - add_library(monero-wallet-gui SHARED ${monero_wallet_gui_sources}) - set_target_properties(monero-wallet-gui PROPERTIES COMPILE_DEFINITIONS "ANDROID") -endif() +target_sources(monero-wallet-gui PRIVATE ${monero_wallet_gui_sources}) set_target_properties(monero-wallet-gui PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" @@ -92,8 +65,6 @@ target_include_directories(monero-wallet-gui PUBLIC ${OPENGL_INCLUDE_DIR}) message(STATUS "OpenGL: include dir at ${OPENGL_INCLUDE_DIR}") message(STATUS "OpenGL: libraries at ${OPENGL_LIBRARIES}") -target_include_directories(monero-wallet-gui PUBLIC ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) - target_include_directories(monero-wallet-gui PUBLIC ${CMAKE_SOURCE_DIR}/monero/include ${CMAKE_SOURCE_DIR}/monero/src @@ -109,12 +80,6 @@ target_include_directories(monero-wallet-gui PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/zxcvbn-c ) -target_compile_definitions(monero-wallet-gui - PUBLIC - ${Qt5Widgets_DEFINITIONS} - ${Qt5Qml_DEFINITIONS} -) - if(QML_TESTS) target_include_directories(monero-wallet-gui PRIVATE "${CMAKE_SOURCE_DIR}/tests/qml") target_compile_definitions(monero-wallet-gui PRIVATE @@ -122,7 +87,6 @@ if(QML_TESTS) QML_TEST_SOURCE_DIR="${CMAKE_SOURCE_DIR}/tests/qml" ) endif() - if(APPLE) if(NOT ICU_ROOT) execute_process(COMMAND brew --prefix icu4c OUTPUT_VARIABLE ICU_ROOT OUTPUT_STRIP_TRAILING_WHITESPACE) @@ -131,16 +95,15 @@ if(APPLE) target_link_directories(monero-wallet-gui PRIVATE ${ICU_ROOT}/lib) endif() -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}") - target_link_libraries(monero-wallet-gui + PRIVATE epee common net wallet_api qrcodegen easylogging - ${QT5_LIBRARIES} + ${QT_LIBRARIES} ${EXTRA_LIBRARIES} openpgp qrdecoder @@ -148,10 +111,17 @@ target_link_libraries(monero-wallet-gui zxcvbn ) +if(MINGW) + # Static Boost.Locale records ICU and iconv as bare library names. Keep the + # depends library search path scoped to the executable that consumes them. + target_link_directories(monero-wallet-gui PRIVATE "${BOOST_LIBRARYDIR}") +endif() + if(WITH_SCANNER) - target_link_libraries(monero-wallet-gui qrscanner) + target_link_libraries(monero-wallet-gui PRIVATE qrscanner) if(LINUX AND NOT ANDROID) target_link_libraries(monero-wallet-gui + PRIVATE jpeg v4l2 v4lconvert @@ -160,10 +130,4 @@ if(WITH_SCANNER) endif() endif() -add_custom_command(TARGET monero-wallet-gui POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ $) - include(Deploy) - -install(TARGETS monero-wallet-gui - DESTINATION bin -) diff --git a/src/QR-Code-scanner/CMakeLists.txt b/src/QR-Code-scanner/CMakeLists.txt index 15e288dfe8..1b08f42a04 100644 --- a/src/QR-Code-scanner/CMakeLists.txt +++ b/src/QR-Code-scanner/CMakeLists.txt @@ -3,7 +3,7 @@ add_library(qrdecoder STATIC ) target_link_libraries(qrdecoder PUBLIC - Qt5::Gui + Qt6::Gui PRIVATE quirc ) @@ -15,7 +15,7 @@ if(WITH_SCANNER) ) target_link_libraries(qrscanner PUBLIC - Qt5::Multimedia + Qt6::Multimedia qrdecoder ) endif() diff --git a/src/QR-Code-scanner/QrCodeScanner.cpp b/src/QR-Code-scanner/QrCodeScanner.cpp index c965a5473b..5eb92971e7 100644 --- a/src/QR-Code-scanner/QrCodeScanner.cpp +++ b/src/QR-Code-scanner/QrCodeScanner.cpp @@ -27,8 +27,10 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "QrCodeScanner.h" -#include #include +#include +#include +#include QrCodeScanner::QrCodeScanner(QObject *parent) : QObject(parent) @@ -36,16 +38,43 @@ QrCodeScanner::QrCodeScanner(QObject *parent) , m_processInterval(750) , m_enabled(true) { - m_probe = new QVideoProbe(this); + m_captureSession = new QMediaCaptureSession(this); + m_sink = nullptr; m_thread = new QrScanThread(this); m_thread->start(); - QObject::connect(m_thread, SIGNAL(decoded(QString)), this, SIGNAL(decoded(QString))); - QObject::connect(m_thread, SIGNAL(notifyError(const QString &, bool)), this, SIGNAL(notifyError(const QString &, bool))); - connect(m_probe, SIGNAL(videoFrameProbed(QVideoFrame)), this, SLOT(processFrame(QVideoFrame))); + connect(m_thread, &QrScanThread::decoded, this, &QrCodeScanner::decoded); + connect(m_thread, &QrScanThread::notifyError, this, &QrCodeScanner::notifyError); } -void QrCodeScanner::setSource(QCamera *camera) + +bool QrCodeScanner::setSource(QObject *camera) { - m_probe->setSource(camera); + QCamera *qmlCamera = qobject_cast(camera); + if (!qmlCamera) { + qWarning() << "QrCodeScanner: source is not a QCamera"; + m_captureSession->setCamera(nullptr); + return false; + } + m_captureSession->setCamera(qmlCamera); + return true; +} +bool QrCodeScanner::setVideoOutput(QObject *videoOutput) +{ + m_captureSession->setVideoOutput(videoOutput); + QVideoSink *sink = m_captureSession->videoSink(); + if (!sink) { + qWarning() << "QrCodeScanner: video output has no QVideoSink"; + m_captureSession->setVideoOutput(nullptr); + m_sink = nullptr; + return false; + } + if (m_sink == sink) + return true; + if (m_sink) + disconnect(m_sink, &QVideoSink::videoFrameChanged, this, &QrCodeScanner::processFrame); + m_sink = sink; + if (m_sink) + connect(m_sink, &QVideoSink::videoFrameChanged, this, &QrCodeScanner::processFrame); + return true; } void QrCodeScanner::processFrame(QVideoFrame frame) { diff --git a/src/QR-Code-scanner/QrCodeScanner.h b/src/QR-Code-scanner/QrCodeScanner.h index ac68ab679f..5151e4df2a 100644 --- a/src/QR-Code-scanner/QrCodeScanner.h +++ b/src/QR-Code-scanner/QrCodeScanner.h @@ -30,11 +30,12 @@ #define QRCODESCANNER_H_ #include +#include #include #include "QrScanThread.h" -class QVideoProbe; -class QCamera; +class QMediaCaptureSession; +class QVideoSink; class QrCodeScanner : public QObject { @@ -45,7 +46,8 @@ class QrCodeScanner : public QObject public: QrCodeScanner(QObject *parent = Q_NULLPTR); ~QrCodeScanner(); - void setSource(QCamera*); + Q_INVOKABLE bool setSource(QObject *camera); + Q_INVOKABLE bool setVideoOutput(QObject *videoOutput); bool enabled() const; void setEnabled(bool enabled); @@ -66,7 +68,8 @@ public Q_SLOTS: int m_processInterval; int m_enabled; QVideoFrame m_curFrame; - QVideoProbe *m_probe; + QMediaCaptureSession *m_captureSession; + QPointer m_sink; }; #endif diff --git a/src/QR-Code-scanner/QrScanThread.cpp b/src/QR-Code-scanner/QrScanThread.cpp index fb251d51e2..7c167a6c7d 100644 --- a/src/QR-Code-scanner/QrScanThread.cpp +++ b/src/QR-Code-scanner/QrScanThread.cpp @@ -27,13 +27,8 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "QrScanThread.h" -#include #include -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) -extern QImage qt_imageFromVideoFrame(const QVideoFrame &f); -#endif - QrScanThread::QrScanThread(QObject *parent) : QThread(parent) ,m_running(true) @@ -56,11 +51,7 @@ void QrScanThread::processQImage(const QImage &qimg) void QrScanThread::processVideoFrame(const QVideoFrame &frame) { -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - processQImage( qt_imageFromVideoFrame(frame) ); -#else - processQImage(frame.image()); -#endif + processQImage(frame.toImage()); } void QrScanThread::stop() diff --git a/src/libwalletqt/TransactionHistory.cpp b/src/libwalletqt/TransactionHistory.cpp index 90085aeecb..ddfbde5afe 100644 --- a/src/libwalletqt/TransactionHistory.cpp +++ b/src/libwalletqt/TransactionHistory.cpp @@ -84,11 +84,7 @@ bool TransactionHistory::transaction(int index, std::function= QT_VERSION_CHECK(5, 14, 0) QDateTime firstDateTime = QDate(2014, 4, 18).startOfDay(); -#else - QDateTime firstDateTime = QDateTime(QDate(2014, 4, 18)); // the genesis block -#endif QDateTime lastDateTime = QDateTime::currentDateTime().addDays(1); // tomorrow (guard against jitter and timezones) emit refreshStarted(); @@ -173,11 +169,7 @@ bool TransactionHistory::TransactionHistory::locked() const TransactionHistory::TransactionHistory(Monero::TransactionHistory *pimpl, QObject *parent) : QObject(parent), m_pimpl(pimpl), m_minutesToUnlock(0), m_locked(false) { -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) m_firstDateTime = QDate(2014, 4, 18).startOfDay(); -#else - m_firstDateTime = QDateTime(QDate(2014, 4, 18)); // the genesis block -#endif m_lastDateTime = QDateTime::currentDateTime().addDays(1); // tomorrow (guard against jitter and timezones) } diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h index 1cac75a505..22b572c75d 100644 --- a/src/libwalletqt/Wallet.h +++ b/src/libwalletqt/Wallet.h @@ -61,6 +61,15 @@ class SubaddressModel; class SubaddressAccount; class SubaddressAccountModel; +Q_MOC_INCLUDE("TransactionHistory.h") +Q_MOC_INCLUDE("model/TransactionHistorySortFilterModel.h") +Q_MOC_INCLUDE("AddressBook.h") +Q_MOC_INCLUDE("model/AddressBookModel.h") +Q_MOC_INCLUDE("Subaddress.h") +Q_MOC_INCLUDE("model/SubaddressModel.h") +Q_MOC_INCLUDE("SubaddressAccount.h") +Q_MOC_INCLUDE("model/SubaddressAccountModel.h") + class Wallet : public QObject, public PassprasePrompter { Q_OBJECT diff --git a/src/main/Logger.cpp b/src/main/Logger.cpp index 8fa9897923..d248831c80 100644 --- a/src/main/Logger.cpp +++ b/src/main/Logger.cpp @@ -38,7 +38,7 @@ #include #include -#include "qt/MoneroSettings.h" +#include "qt/PortableSettings.h" #include "qt/TailsOS.h" // default log path by OS (should be writable) @@ -77,7 +77,7 @@ const QString getLogPath(const QString &userDefinedLogFilePath, bool portable) if (portable) { - return QDir(MoneroSettings::portableFolderName()).filePath(defaultLogName); + return QDir(PortableSettings::portableFolderName()).filePath(defaultLogName); } if(TailsOS::detect() && TailsOS::usePersistence) diff --git a/src/main/filter.cpp b/src/main/filter.cpp index b143e940b5..bfbee7d2c0 100644 --- a/src/main/filter.cpp +++ b/src/main/filter.cpp @@ -82,7 +82,7 @@ bool filter::eventFilter(QObject *obj, QEvent *ev) { sks = "Ctrl"; #endif } else { - QKeySequence ks(ke->modifiers() + ke->key()); + QKeySequence ks(QKeyCombination(ke->modifiers(), static_cast(ke->key()))); sks = ks.toString(); } #ifndef Q_OS_MAC @@ -116,7 +116,7 @@ bool filter::eventFilter(QObject *obj, QEvent *ev) { sks = "Ctrl"; #endif } else { - QKeySequence ks(ke->modifiers() + ke->key()); + QKeySequence ks(QKeyCombination(ke->modifiers(), static_cast(ke->key()))); sks = ks.toString(); } #ifndef Q_OS_MAC @@ -129,11 +129,11 @@ bool filter::eventFilter(QObject *obj, QEvent *ev) { } break; case QEvent::MouseButtonPress: { QMouseEvent *me = static_cast(ev); - emit mousePressed(QVariant::fromValue(obj), me->x(), me->y()); + emit mousePressed(QVariant::fromValue(obj), me->position().x(), me->position().y()); } break; case QEvent::MouseButtonRelease: { QMouseEvent *me = static_cast(ev); - emit mouseReleased(QVariant::fromValue(obj), me->x(), me->y()); + emit mouseReleased(QVariant::fromValue(obj), me->position().x(), me->position().y()); } break; default: break; } diff --git a/src/main/main.cpp b/src/main/main.cpp index 6afbaa83d6..d6a88f704c 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -34,7 +34,6 @@ #include #include #include -#include #include #include @@ -70,7 +69,7 @@ #include "qt/utils.h" #include "qt/TailsOS.h" #include "qt/KeysFiles.h" -#include "qt/MoneroSettings.h" +#include "qt/PortableSettings.h" #include "qt/NetworkAccessBlockingFactory.h" #ifdef QML_TESTS #include "QmlTestHarness.h" @@ -85,9 +84,7 @@ #include "p2pool/P2PoolManager.h" #endif -#if defined(Q_OS_WIN) -#include -#elif defined(Q_OS_MACOS) +#if defined(Q_OS_MACOS) #include "qt/macoshelper.h" #endif @@ -104,7 +101,6 @@ Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); #elif defined(Q_OS_LINUX) Q_IMPORT_PLUGIN(QXcbIntegrationPlugin); - Q_IMPORT_PLUGIN(QXcbGlxIntegrationPlugin); #endif Q_IMPORT_PLUGIN(QSvgIconPlugin) Q_IMPORT_PLUGIN(QICNSPlugin) @@ -115,39 +111,6 @@ Q_IMPORT_PLUGIN(QTgaPlugin) Q_IMPORT_PLUGIN(QTiffPlugin) Q_IMPORT_PLUGIN(QWbmpPlugin) Q_IMPORT_PLUGIN(QWebpPlugin) -Q_IMPORT_PLUGIN(QQmlDebuggerServiceFactory) -Q_IMPORT_PLUGIN(QQmlInspectorServiceFactory) -Q_IMPORT_PLUGIN(QLocalClientConnectionFactory) -Q_IMPORT_PLUGIN(QDebugMessageServiceFactory) -Q_IMPORT_PLUGIN(QQmlNativeDebugConnectorFactory) -Q_IMPORT_PLUGIN(QQmlNativeDebugServiceFactory) -Q_IMPORT_PLUGIN(QQmlProfilerServiceFactory) -Q_IMPORT_PLUGIN(QQuickProfilerAdapterFactory) -Q_IMPORT_PLUGIN(QQmlDebugServerFactory) -Q_IMPORT_PLUGIN(QTcpServerConnectionFactory) -Q_IMPORT_PLUGIN(QGenericEnginePlugin) - -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) -Q_IMPORT_PLUGIN(QtQmlPlugin) -#endif -Q_IMPORT_PLUGIN(QtQmlModelsPlugin) -Q_IMPORT_PLUGIN(QtQuick2Plugin) -Q_IMPORT_PLUGIN(QtQuickLayoutsPlugin) -Q_IMPORT_PLUGIN(QtGraphicalEffectsPlugin) -Q_IMPORT_PLUGIN(QtGraphicalEffectsPrivatePlugin) -Q_IMPORT_PLUGIN(QtQuick2WindowPlugin) -Q_IMPORT_PLUGIN(QtQuickControls1Plugin) -Q_IMPORT_PLUGIN(QtQuick2DialogsPlugin) -Q_IMPORT_PLUGIN(QmlFolderListModelPlugin) -Q_IMPORT_PLUGIN(QmlSettingsPlugin) -Q_IMPORT_PLUGIN(QtLabsPlatformPlugin) -Q_IMPORT_PLUGIN(QtQuick2DialogsPrivatePlugin) -Q_IMPORT_PLUGIN(QtQuick2PrivateWidgetsPlugin) -Q_IMPORT_PLUGIN(QtQuickControls2Plugin) -Q_IMPORT_PLUGIN(QtQuickTemplates2Plugin) -#ifdef WITH_SCANNER -Q_IMPORT_PLUGIN(QMultimediaDeclarativeModule) -#endif #endif @@ -158,7 +121,6 @@ bool isMac = false; bool isLinux = false; bool isTails = false; bool isDesktop = false; -bool isOpenGL = true; bool isARM = false; int main(int argc, char *argv[]) @@ -191,16 +153,14 @@ int main(int argc, char *argv[]) bool isARM = true; #endif - // detect low graphics mode (start-low-graphics-mode.bat) - if(qgetenv("QMLSCENE_DEVICE") == "softwarecontext") - isOpenGL = false; - #ifdef Q_OS_MAC // macOS window tabbing is not supported MacOSHelper::disableWindowTabbing(); #endif // disable "QApplication: invalid style override passed" warning - if (isDesktop) qputenv("QT_STYLE_OVERRIDE", "fusion"); + if (isDesktop) qputenv("QT_STYLE_OVERRIDE", "Fusion"); + if (isDesktop && qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) + qputenv("QT_QUICK_CONTROLS_STYLE", "Fusion"); #ifdef Q_OS_LINUX // platform xcb by default if (isDesktop && qEnvironmentVariableIsEmpty("QT_QPA_PLATFORM")) qputenv("QT_QPA_PLATFORM", "xcb"); @@ -227,17 +187,6 @@ int main(int argc, char *argv[]) MainApp app(argc, argv); -#if defined(Q_OS_WIN) - if (isOpenGL) - { - QOpenGLContext ctx; - isOpenGL = ctx.create() && ctx.format().version() >= qMakePair(2, 1); - if (!isOpenGL) { - qputenv("QMLSCENE_DEVICE", "softwarecontext"); - } - } -#endif - app.setApplicationName("monero-core"); app.setDesktopFileName("org.getmonero.Monero"); app.setOrganizationDomain("getmonero.org"); @@ -394,8 +343,7 @@ Verify update binary using 'shasum'-compatible (SHA256 algo) output signed by tw qmlRegisterType("moneroComponents.LanguageModel", 1, 0, "LanguageModel"); qmlRegisterType("moneroComponents.WalletManager", 1, 0, "WalletManager"); - // Temporary Qt.labs.settings replacement - qmlRegisterType("moneroComponents.Settings", 1, 0, "MoneroSettings"); + qmlRegisterType("moneroComponents.Settings", 1, 0, "PortableSettings"); qmlRegisterUncreatableType("moneroComponents.Wallet", 1, 0, "Wallet", "Wallet can't be instantiated directly"); @@ -457,15 +405,18 @@ Verify update binary using 'shasum'-compatible (SHA256 algo) output signed by tw QQmlApplicationEngine engine; -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) - engine.setNetworkAccessManagerFactory(new NetworkAccessBlockingFactory); + engine.addImportPath(QStringLiteral(":/fonts")); + +#if defined(Q_OS_WIN) && !defined(MONERO_GUI_STATIC) + engine.addImportPath(QCoreApplication::applicationDirPath() + "/qml"); #endif + + engine.setNetworkAccessManagerFactory(new NetworkAccessBlockingFactory); OSCursor cursor; engine.rootContext()->setContextProperty("globalCursor", &cursor); OSHelper osHelper; engine.rootContext()->setContextProperty("oshelper", &osHelper); - engine.addImportPath(":/fonts"); engine.rootContext()->setContextProperty("moneroAccountsDir", moneroAccountsDir); @@ -496,7 +447,6 @@ Verify update binary using 'shasum'-compatible (SHA256 algo) output signed by tw engine.rootContext()->setContextProperty("isLinux", isLinux); engine.rootContext()->setContextProperty("isIOS", isIOS); engine.rootContext()->setContextProperty("isAndroid", isAndroid); - engine.rootContext()->setContextProperty("isOpenGL", isOpenGL); engine.rootContext()->setContextProperty("isTails", isTails); engine.rootContext()->setContextProperty("isARM", isARM); @@ -532,6 +482,7 @@ Verify update binary using 'shasum'-compatible (SHA256 algo) output signed by tw #ifdef WITH_SCANNER builtWithScanner = true; #endif + qInfo() << "QR scanner: compiled in:" << builtWithScanner; engine.rootContext()->setContextProperty("builtWithScanner", builtWithScanner); bool builtWithDesktopEntry = false; @@ -560,19 +511,6 @@ Verify update binary using 'shasum'-compatible (SHA256 algo) output signed by tw if (parser.isSet(testQmlOption)) return 0; -#ifdef WITH_SCANNER - QObject *qmlCamera = rootObject->findChild("qrCameraQML"); - if (qmlCamera) - { - qWarning() << "QrCodeScanner : object found"; - QCamera *camera_ = qvariant_cast(qmlCamera->property("mediaObject")); - QObject *qmlFinder = rootObject->findChild("QrFinder"); - qobject_cast(qmlFinder)->setSource(camera_); - } - else - qCritical() << "QrCodeScanner : something went wrong !"; -#endif - QObject::connect(eventFilter, &filter::quitRequested, rootObject, [rootObject]{ QMetaObject::invokeMethod(rootObject, "gracefulQuit", Qt::QueuedConnection); }); QObject::connect(rootObject, SIGNAL(gracefulShutdownComplete()), eventFilter, SLOT(acceptQuit())); QObject::connect(eventFilter, SIGNAL(sequencePressed(QVariant,QVariant)), rootObject, SLOT(sequencePressed(QVariant,QVariant))); diff --git a/src/model/TransactionHistorySortFilterModel.cpp b/src/model/TransactionHistorySortFilterModel.cpp index bc8d68dc6b..877a0e0e4b 100644 --- a/src/model/TransactionHistorySortFilterModel.cpp +++ b/src/model/TransactionHistorySortFilterModel.cpp @@ -71,6 +71,16 @@ TransactionHistorySortFilterModel::TransactionHistorySortFilterModel(QObject *pa setDynamicSortFilter(true); } +void TransactionHistorySortFilterModel::invalidateRowFilter() +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0) + beginFilterChange(); + endFilterChange(QSortFilterProxyModel::Direction::Rows); +#else + invalidateFilter(); +#endif +} + QString TransactionHistorySortFilterModel::searchFilter() const { return m_searchString; @@ -81,7 +91,7 @@ void TransactionHistorySortFilterModel::setSearchFilter(const QString &arg) if (searchFilter() != arg) { m_searchString = arg; emit searchFilterChanged(); - invalidateFilter(); + invalidateRowFilter(); } } @@ -95,7 +105,7 @@ void TransactionHistorySortFilterModel::setPaymentIdFilter(const QString &arg) if (paymentIdFilter() != arg) { m_filterValues[TransactionHistoryModel::TransactionPaymentIdRole] = arg; emit paymentIdFilterChanged(); - invalidateFilter(); + invalidateRowFilter(); } } @@ -109,7 +119,7 @@ void TransactionHistorySortFilterModel::setDateFromFilter(const QDate &date) if (date != dateFromFilter()) { setScopeFilterValue(m_filterValues, TransactionHistoryModel::TransactionTimeStampRole, ScopeIndex::From, date); emit dateFromFilterChanged(); - invalidateFilter(); + invalidateRowFilter(); } } @@ -123,7 +133,7 @@ void TransactionHistorySortFilterModel::setDateToFilter(const QDate &date) if (date != dateToFilter()) { setScopeFilterValue(m_filterValues, TransactionHistoryModel::TransactionTimeStampRole, ScopeIndex::To, date); emit dateToFilterChanged(); - invalidateFilter(); + invalidateRowFilter(); } } @@ -137,7 +147,7 @@ void TransactionHistorySortFilterModel::setAmountFromFilter(double value) if (value != amountFromFilter()) { setScopeFilterValue(m_filterValues, TransactionHistoryModel::TransactionAmountRole, ScopeIndex::From, value); emit amountFromFilterChanged(); - invalidateFilter(); + invalidateRowFilter(); } } @@ -151,7 +161,7 @@ void TransactionHistorySortFilterModel::setAmountToFilter(double value) if (value != amountToFilter()) { setScopeFilterValue(m_filterValues, TransactionHistoryModel::TransactionAmountRole, ScopeIndex::To, value); emit amountToFilterChanged(); - invalidateFilter(); + invalidateRowFilter(); } } @@ -165,7 +175,7 @@ void TransactionHistorySortFilterModel::setDirectionFilter(int value) if (value != directionFilter()) { m_filterValues[TransactionHistoryModel::TransactionDirectionRole] = QVariant::fromValue(value); emit directionFilterChanged(); - invalidateFilter(); + invalidateRowFilter(); } } @@ -205,14 +215,8 @@ bool TransactionHistorySortFilterModel::filterAcceptsRow(int source_row, const Q break; case TransactionHistoryModel::TransactionTimeStampRole: { -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) QDateTime from = dateFromFilter().startOfDay(); QDateTime to = dateToFilter().endOfDay(); -#else - QDateTime from = QDateTime(dateFromFilter()); - QDateTime to = QDateTime(dateToFilter()); - to = to.addDays(1); // including upperbound -#endif QDateTime timestamp = data.toDateTime(); bool matchFrom = from.isNull() || timestamp.isNull() || timestamp >= from; bool matchTo = to.isNull() || timestamp.isNull() || timestamp <= to; diff --git a/src/model/TransactionHistorySortFilterModel.h b/src/model/TransactionHistorySortFilterModel.h index 66286c55f4..3413e8416b 100644 --- a/src/model/TransactionHistorySortFilterModel.h +++ b/src/model/TransactionHistorySortFilterModel.h @@ -39,6 +39,8 @@ class TransactionHistory; +Q_MOC_INCLUDE("TransactionHistory.h") + class TransactionHistorySortFilterModel: public QSortFilterProxyModel { Q_OBJECT @@ -101,6 +103,8 @@ class TransactionHistorySortFilterModel: public QSortFilterProxyModel private: + void invalidateRowFilter(); + enum ScopeIndex { From = 0, To = 1 diff --git a/src/qml-resources.cmake b/src/qml-resources.cmake new file mode 100644 index 0000000000..aeafc82cb2 --- /dev/null +++ b/src/qml-resources.cmake @@ -0,0 +1,80 @@ +file(GLOB GUI_ROOT_QML_FILES CONFIGURE_DEPENDS + "${CMAKE_SOURCE_DIR}/*.js" + "${CMAKE_SOURCE_DIR}/*.qml" +) +file(GLOB_RECURSE GUI_DIRECTORY_QML_FILES CONFIGURE_DEPENDS + "${CMAKE_SOURCE_DIR}/components/*.qml" + "${CMAKE_SOURCE_DIR}/fonts/FontAwesome/*.qml" + "${CMAKE_SOURCE_DIR}/js/*.js" + "${CMAKE_SOURCE_DIR}/pages/*.qml" + "${CMAKE_SOURCE_DIR}/wizard/*.qml" +) +set(GUI_QML_FILES ${GUI_ROOT_QML_FILES} ${GUI_DIRECTORY_QML_FILES}) + +set(SCANNER_QML_FILE "${CMAKE_SOURCE_DIR}/components/QRCodeScanner.qml") +if(NOT WITH_SCANNER) + list(REMOVE_ITEM GUI_QML_FILES "${SCANNER_QML_FILE}") +endif() + +file(GLOB GUI_IMAGE_FILES CONFIGURE_DEPENDS + "${CMAKE_SOURCE_DIR}/images/*.ico" + "${CMAKE_SOURCE_DIR}/images/*.png" + "${CMAKE_SOURCE_DIR}/images/*.svg" + "${CMAKE_SOURCE_DIR}/images/merchant/*.png" + "${CMAKE_SOURCE_DIR}/images/themes/white/*.png" +) +file(GLOB GUI_FONT_FILES CONFIGURE_DEPENDS + "${CMAKE_SOURCE_DIR}/fonts/*.ttf" + "${CMAKE_SOURCE_DIR}/fonts/FontAwesome/*.otf" +) +file(GLOB GUI_LANGUAGE_FILES CONFIGURE_DEPENDS + "${CMAKE_SOURCE_DIR}/lang/flags/*.png" +) + +set(GUI_ASSET_FILES + ${GUI_IMAGE_FILES} + ${GUI_FONT_FILES} + ${GUI_LANGUAGE_FILES} + "${CMAKE_SOURCE_DIR}/components/qmldir" + "${CMAKE_SOURCE_DIR}/fonts/FontAwesome/qmldir" + "${CMAKE_SOURCE_DIR}/images/themes/white/close.svg" + "${CMAKE_SOURCE_DIR}/images/themes/white/fullscreen.svg" + "${CMAKE_SOURCE_DIR}/images/themes/white/minimize.svg" + "${CMAKE_SOURCE_DIR}/lang/languages.xml" + "${CMAKE_SOURCE_DIR}/monero/utils/gpg_keys/binaryfate.asc" + "${CMAKE_SOURCE_DIR}/monero/utils/gpg_keys/fluffypony.asc" + "${CMAKE_SOURCE_DIR}/monero/utils/gpg_keys/luigi1111.asc" + "${CMAKE_SOURCE_DIR}/qtquickcontrols2.conf" + "${CMAKE_SOURCE_DIR}/wizard/template.pdf" +) + +set(GUI_RESOURCE_ALIASES) +foreach(RESOURCE_FILE IN LISTS GUI_QML_FILES GUI_ASSET_FILES) + if(NOT EXISTS "${RESOURCE_FILE}") + message(FATAL_ERROR "Missing QML/resource file: ${RESOURCE_FILE}") + endif() + + file(RELATIVE_PATH RESOURCE_ALIAS "${CMAKE_SOURCE_DIR}" "${RESOURCE_FILE}") + if(RESOURCE_ALIAS IN_LIST GUI_RESOURCE_ALIASES) + message(FATAL_ERROR "Duplicate QML/resource alias: ${RESOURCE_ALIAS}") + endif() + + list(APPEND GUI_RESOURCE_ALIASES "${RESOURCE_ALIAS}") + set_source_files_properties("${RESOURCE_FILE}" PROPERTIES + QT_RESOURCE_ALIAS "${RESOURCE_ALIAS}" + ) +endforeach() + +set(GUI_QML_SINGLETONS + "${CMAKE_SOURCE_DIR}/fonts/FontAwesome/FontAwesome.qml" + "${CMAKE_SOURCE_DIR}/components/Style.qml" +) +set_source_files_properties(${GUI_QML_FILES} PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(${GUI_QML_SINGLETONS} PROPERTIES + QT_QML_SINGLETON_TYPE TRUE +) +set_source_files_properties("${CMAKE_SOURCE_DIR}/components/Style.qml" PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY FALSE +) diff --git a/src/qt/KeysFiles.cpp b/src/qt/KeysFiles.cpp index e2f57d0fa3..f24cd54b5d 100644 --- a/src/qt/KeysFiles.cpp +++ b/src/qt/KeysFiles.cpp @@ -126,7 +126,7 @@ void WalletKeysFilesModel::findWallets(const QString &moneroAccountsDir) quint8 networkType = networkTypeAndAddress.first; QString address = networkTypeAndAddress.second; - this->addWalletKeysFile(WalletKeysFiles(wallet, networkType, std::move(address))); + this->addWalletKeysFile(WalletKeysFiles(QFileInfo(wallet), networkType, std::move(address))); } } diff --git a/src/qt/MoneroSettings.cpp b/src/qt/MoneroSettings.cpp deleted file mode 100644 index c46e4cf504..0000000000 --- a/src/qt/MoneroSettings.cpp +++ /dev/null @@ -1,290 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -****************************************************************************/ -// Copyright (c) 2014-2024, The Monero Project -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include -#include -#include -#include -#include -#include -#include - -#include "qt/MoneroSettings.h" - -/*! - \qmlmodule moneroSettings 1.0 - \title Monero Settings QML Component - \ingroup qmlmodules - \brief Provides persistent platform-independent application settings. - - This component was introduced in order to have control over where the - configuration file is written. This is needed for Tails OS and - portable installations. - - For more information, see: https://doc.qt.io/qt-5/qml-qt-labs-settings-settings.html and - https://github.com/qt/qtdeclarative/blob/v5.12.0/src/imports/settings/qqmlsettings.cpp - - To use this module, import the module with the following line: - \code - import moneroComponents.Settings 1.0 - \endcode - - Usage: - \code - MoneroSettings { id: persistentSettings, property bool foo: true } - \endcode - - @TODO: Remove this QML component after migrating to Qt >= 5.12.0, as - `Qt.labs.settings` provides the fileName via a Q_PROPERTY -*/ - - -void MoneroSettings::load() -{ - const QMetaObject *mo = this->metaObject(); - const int offset = mo->propertyOffset(); - const int count = mo->propertyCount(); - - for (int i = offset; i < count; ++i) { - QMetaProperty property = mo->property(i); - const QVariant previousValue = readProperty(property); - const QVariant currentValue = this->m_settings->value(property.name(), previousValue); - - if (!currentValue.isNull() && (!previousValue.isValid() - || (currentValue.canConvert(previousValue.type()) && previousValue != currentValue))) { - property.write(this, currentValue); -#ifdef QT_DEBUG - qDebug() << "QQmlSettings: load" << property.name() << "setting:" << currentValue << "default:" << previousValue; -#endif - } - - // ensure that a non-existent setting gets written - // even if the property wouldn't change later - if (!this->m_settings->contains(property.name())) - this->_q_propertyChanged(); - - // setup change notifications on first load - if (!this->m_initialized && property.hasNotifySignal()) { - static const int propertyChangedIndex = mo->indexOfSlot("_q_propertyChanged()"); - int signalIndex = property.notifySignalIndex(); - QMetaObject::connect(this, signalIndex, this, propertyChangedIndex); - } - } -} - -void MoneroSettings::_q_propertyChanged() -{ - // Called on QML property change - const QMetaObject *mo = this->metaObject(); - const int offset = mo->propertyOffset(); - const int count = mo->propertyCount(); - for (int i = offset; i < count; ++i) { - const QMetaProperty &property = mo->property(i); - const QVariant value = readProperty(property); - this->m_changedProperties.insert(property.name(), value); -#ifdef QT_DEBUG - //qDebug() << "QQmlSettings: cache" << property.name() << ":" << value; -#endif - } - - if (this->m_timerId != 0) - this->killTimer(this->m_timerId); - this->m_timerId = this->startTimer(settingsWriteDelay); -} - -QVariant MoneroSettings::readProperty(const QMetaProperty &property) const -{ - QVariant var = property.read(this); - if (var.userType() == qMetaTypeId()) - var = var.value().toVariant(); - return var; -} - -void MoneroSettings::init() -{ - if (!this->m_initialized) { - this->m_settings = portableConfigExists() ? portableSettings() : unportableSettings(); -#ifdef QT_DEBUG - qDebug() << "QQmlSettings: stored at" << this->m_settings->fileName(); -#endif - this->load(); - this->m_initialized = true; - emit portableChanged(); - } -} - -void MoneroSettings::reset() -{ - if (this->m_initialized && this->m_settings && !this->m_changedProperties.isEmpty()) - this->store(); - if (this->m_settings) - this->m_settings.reset(); -} - -void MoneroSettings::store() -{ - if (!m_writable) - { - return; - } - - QHash::const_iterator it = this->m_changedProperties.constBegin(); - - while (it != this->m_changedProperties.constEnd()) { - this->m_settings->setValue(it.key(), it.value()); - -#ifdef QT_DEBUG - //qDebug() << "QQmlSettings: store" << it.key() << ":" << it.value(); -#endif - - ++it; - } - - this->m_changedProperties.clear(); -} - -bool MoneroSettings::portable() const -{ - return this->m_settings && this->m_settings->fileName() == portableFilePath(); -} - -bool MoneroSettings::portableConfigExists() -{ - QFileInfo info(portableFilePath()); - return info.exists() && info.isFile(); -} - -QString MoneroSettings::portableFilePath() -{ - static QString filename(QDir(portableFolderName()).absoluteFilePath("settings.ini")); - return filename; -} - -QString MoneroSettings::portableFolderName() -{ - return "monero-storage"; -} - -std::unique_ptr MoneroSettings::portableSettings() const -{ - return std::unique_ptr(new QSettings(portableFilePath(), QSettings::IniFormat)); -} - -std::unique_ptr MoneroSettings::unportableSettings() const -{ - if (this->m_fileName.isEmpty()) - { - return std::unique_ptr(new QSettings()); - } - return std::unique_ptr(new QSettings(this->m_fileName, QSettings::IniFormat)); -} - -void MoneroSettings::swap(std::unique_ptr newSettings) -{ - const QMetaObject *mo = this->metaObject(); - const int count = mo->propertyCount(); - for (int offset = mo->propertyOffset(); offset < count; ++offset) - { - const QMetaProperty &property = mo->property(offset); - const QVariant value = readProperty(property); - newSettings->setValue(property.name(), value); - } - - this->m_settings.swap(newSettings); - this->m_settings->sync(); - emit portableChanged(); -} - -void MoneroSettings::setFileName(const QString &fileName) -{ - if (fileName != this->m_fileName) { - this->reset(); - this->m_fileName = fileName; - if (this->m_initialized) - this->load(); - } -} - -QString MoneroSettings::fileName() const -{ - return this->m_fileName; -} - -bool MoneroSettings::setPortable(bool enabled) -{ - std::unique_ptr newSettings = enabled ? portableSettings() : unportableSettings(); - if (newSettings->status() != QSettings::NoError) - { - return false; - } - - setWritable(true); - swap(std::move(newSettings)); - - if (!enabled) - { - QFile::remove(portableFilePath()); - } - - return true; -} - -void MoneroSettings::setWritable(bool enabled) -{ - m_writable = enabled; -} - -void MoneroSettings::timerEvent(QTimerEvent *event) -{ - if (event->timerId() == this->m_timerId) { - killTimer(this->m_timerId); - this->m_timerId = 0; - this->store(); - } - QObject::timerEvent(event); -} - -void MoneroSettings::componentComplete() -{ - this->init(); -} - -void MoneroSettings::classBegin() -{ -} - -MoneroSettings::MoneroSettings(QObject *parent) : - QObject(parent) -{ -} diff --git a/src/qt/NetworkAccessBlockingFactory.h b/src/qt/NetworkAccessBlockingFactory.h index 16cbc7f5f9..b9c0dd2a62 100644 --- a/src/qt/NetworkAccessBlockingFactory.h +++ b/src/qt/NetworkAccessBlockingFactory.h @@ -30,6 +30,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* + * Modified for Monero GUI. + */ + /* Through the QQmlNetworkAccessManagerFactory below, all network requests * created via QML will be passed to this object; including, for example, * tags parsed in rich Text items. @@ -39,18 +43,22 @@ * and assert if appropriate. */ #include +#include +#include +#include +#include +#include class BlockedNetworkAccessManager : public QNetworkAccessManager { public: - BlockedNetworkAccessManager(QObject *parent) + explicit BlockedNetworkAccessManager(QObject *parent) : QNetworkAccessManager(parent) { - setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QLatin1String("0.0.0.0"), 0)); } protected: - virtual QNetworkReply *createRequest(Operation op, const QNetworkRequest &req, QIODevice *outgoingData = 0) + QNetworkReply *createRequest(Operation op, const QNetworkRequest &req, QIODevice *outgoingData = nullptr) override { qCritical() << "QML attempted to load a network resource from" << req.url() << " - this is potentially an input sanitization flaw."; return QNetworkAccessManager::createRequest(op, QNetworkRequest(), outgoingData); @@ -60,7 +68,7 @@ class BlockedNetworkAccessManager : public QNetworkAccessManager class NetworkAccessBlockingFactory : public QQmlNetworkAccessManagerFactory { public: - virtual QNetworkAccessManager *create(QObject *parent) + QNetworkAccessManager *create(QObject *parent) override { return new BlockedNetworkAccessManager(parent); } diff --git a/src/qt/PortableSettings.cpp b/src/qt/PortableSettings.cpp new file mode 100644 index 0000000000..216094c016 --- /dev/null +++ b/src/qt/PortableSettings.cpp @@ -0,0 +1,187 @@ +// Copyright (c) 2026, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "qt/PortableSettings.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace +{ +using SettingsMap = QMap; + +bool readSettings(QSettings &settings, SettingsMap &values) +{ + settings.setFallbacksEnabled(false); + settings.sync(); + + if (settings.status() != QSettings::NoError) + return false; + + for (const QString &key : settings.allKeys()) + values.insert(key, settings.value(key)); + + return true; +} + +bool replaceSettings(QSettings &settings, const SettingsMap &values) +{ + settings.setFallbacksEnabled(false); + settings.setAtomicSyncRequired(true); + settings.clear(); + + for (auto it = values.cbegin(); it != values.cend(); ++it) + settings.setValue(it.key(), it.value()); + + settings.sync(); + return settings.status() == QSettings::NoError; +} +} + +PortableSettings::PortableSettings(QObject *parent) + : QObject(parent) + , m_portable(portableConfigExists()) +{ +} + +QString PortableSettings::unportableFileName() const +{ + return m_unportableFileName; +} + +void PortableSettings::setUnportableFileName(const QString &fileName) +{ + if (m_unportableFileName == fileName) + return; + + m_unportableFileName = fileName; + if (!m_portable) + emit locationChanged(); +} + +bool PortableSettings::portable() const +{ + return m_portable; +} + +QUrl PortableSettings::location() const +{ + if (m_portable) + return QUrl::fromLocalFile(portableFilePath()); + if (!m_unportableFileName.isEmpty()) + return QUrl::fromLocalFile(QFileInfo(m_unportableFileName).absoluteFilePath()); + return {}; +} + +bool PortableSettings::setPortable(bool enabled) +{ + if (enabled == m_portable) + return true; + + SettingsMap values; + { + QSettings source = makeSettings(m_portable); + if (!readSettings(source, values)) + return false; + } + + { + QSettings destination = makeSettings(enabled); + if (!replaceSettings(destination, values)) + return false; + } + + if (!setPortableMarker(enabled)) + return false; + + m_portable = enabled; + emit portableChanged(); + emit locationChanged(); + return true; +} + +QString PortableSettings::portableFolderName() +{ + return QStringLiteral("monero-storage"); +} + +bool PortableSettings::portableConfigExists() +{ + const QFileInfo marker(portableMarkerPath()); + return marker.exists() && marker.isFile(); +} + +QString PortableSettings::portableFilePath() +{ + return QDir(portableFolderName()).absoluteFilePath(QStringLiteral("settings.ini")); +} + +QString PortableSettings::portableMarkerPath() +{ + return QDir(portableFolderName()).absoluteFilePath(QStringLiteral(".portable")); +} + +bool PortableSettings::setPortableMarker(bool enabled) +{ + const QString path = portableMarkerPath(); + + if (!enabled) + return !QFile::exists(path) || QFile::remove(path); + + const QFileInfo info(path); + QDir directory(info.absolutePath()); + if (!directory.exists() && !directory.mkpath(QStringLiteral("."))) + return false; + + QSaveFile marker(path); + marker.setDirectWriteFallback(false); + if (!marker.open(QIODevice::WriteOnly)) + return false; + + const QByteArray markerContents("portable\n"); + if (marker.write(markerContents) != markerContents.size()) { + marker.cancelWriting(); + return false; + } + + return marker.commit(); +} + +QSettings PortableSettings::makeSettings(bool portable) const +{ + if (portable) + return QSettings(portableFilePath(), QSettings::IniFormat); + if (!m_unportableFileName.isEmpty()) + return QSettings(m_unportableFileName, QSettings::IniFormat); + return QSettings(); +} diff --git a/src/qt/MoneroSettings.h b/src/qt/PortableSettings.h similarity index 54% rename from src/qt/MoneroSettings.h rename to src/qt/PortableSettings.h index 0ae99b9fa1..c53505eaea 100644 --- a/src/qt/MoneroSettings.h +++ b/src/qt/PortableSettings.h @@ -1,10 +1,4 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -****************************************************************************/ -// Copyright (c) 2014-2024, The Monero Project +// Copyright (c) 2026, The Monero Project // // All rights reserved. // @@ -32,70 +26,45 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#pragma once -#ifndef MONEROSETTINGS_H -#define MONEROSETTINGS_H - -#include - -#include -#include -#include #include -#include -#include +#include +#include -static const int settingsWriteDelay = 500; // ms +class QSettings; -class MoneroSettings : public QObject, public QQmlParserStatus +class PortableSettings : public QObject { Q_OBJECT - Q_INTERFACES(QQmlParserStatus) - Q_PROPERTY(QString fileName READ fileName WRITE setFileName FINAL) + Q_PROPERTY(QString unportableFileName READ unportableFileName WRITE setUnportableFileName NOTIFY locationChanged) Q_PROPERTY(bool portable READ portable NOTIFY portableChanged) Q_PROPERTY(QString portableFolderName READ portableFolderName CONSTANT) + Q_PROPERTY(QUrl location READ location NOTIFY locationChanged) public: - explicit MoneroSettings(QObject *parent = nullptr); + explicit PortableSettings(QObject *parent = nullptr); + + QString unportableFileName() const; + void setUnportableFileName(const QString &fileName); - QString fileName() const; - void setFileName(const QString &fileName); + bool portable() const; + QUrl location() const; Q_INVOKABLE bool setPortable(bool enabled); - Q_INVOKABLE void setWritable(bool enabled); static QString portableFolderName(); - static bool portableConfigExists(); - -public slots: - void _q_propertyChanged(); signals: - void portableChanged() const; - -protected: - void timerEvent(QTimerEvent *event) override; - void classBegin() override; - void componentComplete() override; + void portableChanged(); + void locationChanged(); private: - QVariant readProperty(const QMetaProperty &property) const; - void init(); - void reset(); - void load(); - void store(); - - bool portable() const; + static bool portableConfigExists(); static QString portableFilePath(); - std::unique_ptr portableSettings() const; - std::unique_ptr unportableSettings() const; - void swap(std::unique_ptr newSettings); + static QString portableMarkerPath(); + static bool setPortableMarker(bool enabled); + QSettings makeSettings(bool portable) const; - QHash m_changedProperties; - std::unique_ptr m_settings; - QString m_fileName = QString(""); - bool m_initialized = false; - bool m_writable = true; - int m_timerId = 0; + QString m_unportableFileName; + bool m_portable; }; - -#endif // MONEROSETTINGS_H diff --git a/src/qt/ipc.cpp b/src/qt/ipc.cpp index 82065729a3..8901683e84 100644 --- a/src/qt/ipc.cpp +++ b/src/qt/ipc.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include "ipc.h" diff --git a/src/qt/macoshelper.mm b/src/qt/macoshelper.mm index eb96ef8487..bdf75ebd08 100644 --- a/src/qt/macoshelper.mm +++ b/src/qt/macoshelper.mm @@ -30,7 +30,6 @@ #include #include -#include #include "macoshelper.h" #import @@ -80,5 +79,5 @@ { return {}; } - return QString::fromCFString(reinterpret_cast(bundlePathString)); + return QString::fromNSString(bundlePathString); } diff --git a/src/qt/network.h b/src/qt/network.h index 70f171939c..7a3fa2e360 100644 --- a/src/qt/network.h +++ b/src/qt/network.h @@ -29,7 +29,6 @@ #pragma once #include -#include // TODO: wallet_merged - epee library triggers the warnings #pragma GCC diagnostic push @@ -49,6 +48,11 @@ class HttpClient : public QObject, public net::http::client public: HttpClient(QObject *parent = nullptr); + // Prevent Qt's metatype comparison detection from selecting the generic + // network_address operators inherited through net::http::client. + bool operator==(const HttpClient &) const = delete; + bool operator<(const HttpClient &) const = delete; + void cancel(); quint64 contentLength() const; quint64 received() const; diff --git a/src/qt/updater.cpp b/src/qt/updater.cpp index 14fc87e68a..9908ddb64c 100644 --- a/src/qt/updater.cpp +++ b/src/qt/updater.cpp @@ -26,8 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#if defined(_WIN32) && !defined(MONERO_GUI_STATIC) - #include +#if defined(_WIN32) + #include + #undef interface #endif #include "updater.h" diff --git a/start-low-graphics-mode.bat b/start-low-graphics-mode.bat index c508e1eb3e..e1898a6e27 100644 --- a/start-low-graphics-mode.bat +++ b/start-low-graphics-mode.bat @@ -1,5 +1,5 @@ @echo off -set QMLSCENE_DEVICE=softwarecontext +set QT_QUICK_BACKEND=software start /b monero-wallet-gui.exe diff --git a/tests/qml/QmlTestHarness.cpp b/tests/qml/QmlTestHarness.cpp index cd00465c89..b51d883c18 100644 --- a/tests/qml/QmlTestHarness.cpp +++ b/tests/qml/QmlTestHarness.cpp @@ -28,13 +28,16 @@ #include "QmlTestHarness.h" +#include #include +#include #include #include +#include #include #include #include -#include +#include #include "TranslationManager.h" #include "libwalletqt/Wallet.h" @@ -42,13 +45,42 @@ #include "main/clipboardAdapter.h" #include "main/oshelper.h" #include "qt/KeysFiles.h" +#include "qt/PortableSettings.h" class QmlTestSetup : public QObject { Q_OBJECT public: - QmlTestSetup() : m_accountsDir(QDir::tempPath() + QStringLiteral("/monero-gui-qml-test-XXXXXX")) {} + QmlTestSetup() : m_accountsDir(QDir::tempPath() + QStringLiteral("/monero-gui-qml-test-XXXXXX")) + { + QDir::setCurrent(m_accountsDir.path()); + } + + Q_INVOKABLE bool writeSetting(const QString &path, const QString &key, const QVariant &value) + { + QSettings settings(path, QSettings::IniFormat); + settings.setValue(key, value); + settings.sync(); + return settings.status() == QSettings::NoError; + } + + Q_INVOKABLE QVariant readSetting(const QString &path, const QString &key) + { + QSettings settings(path, QSettings::IniFormat); + return settings.value(key); + } + + Q_INVOKABLE bool containsSetting(const QString &path, const QString &key) + { + QSettings settings(path, QSettings::IniFormat); + return settings.contains(key); + } + + Q_INVOKABLE bool fileExists(const QString &path) + { + return QFile::exists(path); + } public slots: void qmlEngineAvailable(QQmlEngine *engine) @@ -56,15 +88,21 @@ public slots: qmlRegisterType("moneroComponents.Clipboard", 1, 0, "Clipboard"); qmlRegisterType("moneroComponents.WalletKeysFilesModel", 1, 0, "WalletKeysFilesModel"); qmlRegisterType("moneroComponents.WalletManager", 1, 0, "WalletManager"); + qmlRegisterType("moneroComponents.Settings", 1, 0, "PortableSettings"); qmlRegisterUncreatableType("moneroComponents.Wallet", 1, 0, "Wallet", "Wallet can't be instantiated directly"); qmlRegisterType("moneroComponents.NetworkType", 1, 0, "NetworkType"); engine->addImportPath(QStringLiteral(":/fonts")); +#ifdef Q_OS_WIN + engine->addImportPath(QCoreApplication::applicationDirPath() + QStringLiteral("/qml")); +#endif engine->rootContext()->setContextProperty(QStringLiteral("translationManager"), TranslationManager::instance()); engine->rootContext()->setContextProperty(QStringLiteral("oshelper"), &m_osHelper); engine->rootContext()->setContextProperty( QStringLiteral("moneroAccountsDir"), QDir(m_accountsDir.path()).filePath(QStringLiteral("Monero/wallets"))); + engine->rootContext()->setContextProperty(QStringLiteral("moneroTestRoot"), m_accountsDir.path()); + engine->rootContext()->setContextProperty(QStringLiteral("settingsTestHelper"), this); engine->rootContext()->setContextProperty(QStringLiteral("defaultAccountName"), QStringLiteral("qml-test-wallet")); engine->rootContext()->setContextProperty(QStringLiteral("isAndroid"), false); engine->rootContext()->setContextProperty(QStringLiteral("isIOS"), false); @@ -72,7 +110,6 @@ public slots: engine->rootContext()->setContextProperty(QStringLiteral("isMac"), false); engine->rootContext()->setContextProperty(QStringLiteral("isWindows"), false); engine->rootContext()->setContextProperty(QStringLiteral("isTails"), false); - engine->rootContext()->setContextProperty(QStringLiteral("isOpenGL"), false); engine->rootContext()->setContextProperty(QStringLiteral("qtRuntimeVersion"), QString::fromLatin1(qVersion())); } @@ -98,6 +135,9 @@ bool runQmlTestsIfRequested(int argc, char *argv[], int &result) if (!requested) return false; + if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) + qputenv("QT_QUICK_CONTROLS_STYLE", "Fusion"); + int testArgc = testArgv.size(); QmlTestSetup setup; result = quick_test_main_with_setup( diff --git a/tests/qml/tst_Wizard.qml b/tests/qml/tst_Wizard.qml index 613cb58c22..0c84491e7e 100644 --- a/tests/qml/tst_Wizard.qml +++ b/tests/qml/tst_Wizard.qml @@ -26,10 +26,11 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtTest 1.2 +import QtQuick +import QtTest import moneroComponents.NetworkType 1.0 +import moneroComponents.Settings 1.0 import moneroComponents.Wallet 1.0 import moneroComponents.WalletManager 1.0 @@ -42,6 +43,7 @@ Item { height: 800 property alias persistentSettings: persistentSettings + property alias portableSettings: diskPortableSettings property alias wizard: wizardController property string accountsDir: moneroAccountsDir property int walletMode: persistentSettings.walletMode @@ -56,6 +58,9 @@ Item { property bool qrScannerEnabled: false property bool hideBalanceForced: false property bool active: true + property string testSettingsPath: moneroTestRoot + "/settings.ini" + property string portableTestSettingsPath: moneroTestRoot + "/monero-storage/settings.ini" + property string portableMarkerTestPath: moneroTestRoot + "/monero-storage/.portable" function updateBalance() {} @@ -63,6 +68,10 @@ Item { function releaseFocus() {} + function changeWalletMode(mode) { + persistentSettings.walletMode = mode + } + function openWallet() { passwordDialog.open(persistentSettings.wallet_path) } @@ -88,6 +97,17 @@ Item { property bool useRemoteNode: false function setPortable() { return true } function setWritable() { return true } + function sync() {} + } + + QtObject { + id: logger + function resetLogFilePath() {} + } + + PortableSettings { + id: diskPortableSettings + unportableFileName: appWindow.testSettingsPath } QtObject { @@ -265,6 +285,7 @@ Item { var openWalletView = wizardController.wizardStateView.wizardOpenWallet1View tryVerify(function() { return openWalletView.walletCount > 0 }) + verify(waitForPolish(openWalletView)) var recentWallet = null for (var i = 0; i < openWalletView.recentWallets.count; ++i) { var candidate = openWalletView.recentWallets.itemAt(i) @@ -293,6 +314,7 @@ Item { } function init() { + failOnWarning(/.?/) appWindow.ctrlPressed = false appWindow.walletCreated = false appWindow.walletOpenRequested = false @@ -311,6 +333,58 @@ Item { showWizardHome() } + function test_portable_mode_through_wizard() { + verify(!diskPortableSettings.portable) + verify(settingsTestHelper.writeSetting( + appWindow.testSettingsPath, "language", "English (US)")) + verify(settingsTestHelper.writeSetting( + appWindow.testSettingsPath, "walletMode", 2)) + verify(settingsTestHelper.writeSetting( + appWindow.portableTestSettingsPath, "obsolete", "must be removed")) + + var wizardHome = wizardController.wizardStateView.wizardHomeView + wizardHome.changeWalletModeButton.doClick() + tryCompare(wizardController, "wizardState", "wizardModeSelection") + + var modeSelection = wizardController.wizardStateView.wizardModeSelectionView + modeSelection.portableModeButton.menuClicked() + compare(modeSelection.portable, true) + modeSelection.advancedModeButton.menuClicked() + tryCompare(wizardController, "wizardState", "wizardHome") + + verify(diskPortableSettings.portable) + verify(settingsTestHelper.fileExists(appWindow.portableMarkerTestPath)) + compare(settingsTestHelper.readSetting( + appWindow.portableTestSettingsPath, "language"), "English (US)") + compare(Number(settingsTestHelper.readSetting( + appWindow.portableTestSettingsPath, "walletMode")), 2) + verify(!settingsTestHelper.containsSetting( + appWindow.portableTestSettingsPath, "obsolete")) + + verify(settingsTestHelper.writeSetting( + appWindow.testSettingsPath, "obsolete", "must be removed")) + + wizardHome = wizardController.wizardStateView.wizardHomeView + wizardHome.changeWalletModeButton.doClick() + tryCompare(wizardController, "wizardState", "wizardModeSelection") + + modeSelection = wizardController.wizardStateView.wizardModeSelectionView + modeSelection.portableModeButton.menuClicked() + compare(modeSelection.portable, false) + modeSelection.advancedModeButton.menuClicked() + tryCompare(wizardController, "wizardState", "wizardHome") + + verify(!diskPortableSettings.portable) + compare(settingsTestHelper.readSetting( + appWindow.testSettingsPath, "language"), "English (US)") + compare(Number(settingsTestHelper.readSetting( + appWindow.testSettingsPath, "walletMode")), 2) + verify(!settingsTestHelper.containsSetting( + appWindow.testSettingsPath, "obsolete")) + verify(!settingsTestHelper.fileExists(appWindow.portableMarkerTestPath)) + verify(settingsTestHelper.fileExists(appWindow.portableTestSettingsPath)) + } + function test_create_password_wallet_and_open_it() { var createdWallet = createWalletThroughWizard( "wizard-password-test", "correct horse battery staple") diff --git a/translations/CMakeLists.txt b/translations/CMakeLists.txt index 183471de18..82d6db3e08 100644 --- a/translations/CMakeLists.txt +++ b/translations/CMakeLists.txt @@ -1,9 +1,6 @@ -find_package(Qt5Core REQUIRED) +find_package(Qt6 ${QT_MIN_VERSION} REQUIRED COMPONENTS Core LinguistTools) -find_package(Qt5LinguistTools QUIET) -if(NOT Qt5_LRELEASE_EXECUTABLE) - find_program(Qt5_LRELEASE_EXECUTABLE lrelease REQUIRED CMAKE_FIND_ROOT_PATH_BOTH) -endif() +set(LRELEASE_EXECUTABLE $) file(GLOB TS_FILES *.ts) @@ -16,7 +13,7 @@ foreach(TS_FILE ${TS_FILES}) add_custom_command( OUTPUT ${QM_FILE} - COMMAND ${Qt5_LRELEASE_EXECUTABLE} -compress -nounfinished -removeidentical ${TS_FILE} -qm ${QM_FILE} + COMMAND ${LRELEASE_EXECUTABLE} -compress -nounfinished -removeidentical ${TS_FILE} -qm ${QM_FILE} DEPENDS ${TS_FILE} ) @@ -32,8 +29,8 @@ set_source_files_properties(${TRANSLATIONS_QRC} PROPERTIES SKIP_AUTORCC ON) set(TRANSLATIONS_CPP ${CMAKE_CURRENT_BINARY_DIR}/qrc_translations.cpp) add_custom_command( OUTPUT ${TRANSLATIONS_CPP} - COMMAND ${Qt5Core_RCC_EXECUTABLE} - ARGS --name translations --output ${TRANSLATIONS_CPP} ${TRANSLATIONS_QRC} + COMMAND $ + ARGS --compress-algo zlib --name translations --output ${TRANSLATIONS_CPP} ${TRANSLATIONS_QRC} MAIN_DEPENDENCY ${TRANSLATIONS_QRC} DEPENDS ${QM_FILES} VERBATIM @@ -42,4 +39,4 @@ set_source_files_properties(${TRANSLATIONS_CPP} PROPERTIES SKIP_AUTOMOC ON) set_source_files_properties(${TRANSLATIONS_CPP} PROPERTIES SKIP_AUTOUIC ON) add_library(translations ${TRANSLATIONS_CPP}) -target_link_libraries(translations PUBLIC Qt5::Core) +target_link_libraries(translations PUBLIC Qt6::Core) diff --git a/wizard/SeedListGrid.qml b/wizard/SeedListGrid.qml index 74355f93e3..8bb2152fa7 100644 --- a/wizard/SeedListGrid.qml +++ b/wizard/SeedListGrid.qml @@ -1,7 +1,7 @@ -import QtQuick 2.9 -import QtQuick.Dialogs 1.2 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Dialogs +import QtQuick.Layouts +import QtQuick.Controls import "../js/Wizard.js" as Wizard import "../js/Utils.js" as Utils diff --git a/wizard/SeedListItem.qml b/wizard/SeedListItem.qml index 71212cbd22..acfb687233 100644 --- a/wizard/SeedListItem.qml +++ b/wizard/SeedListItem.qml @@ -1,7 +1,9 @@ -import "../components" as MoneroComponents; -import QtQuick 2.9 -import QtQuick.Layouts 1.2 -import FontAwesome 1.0 +import QtQuick +import QtQuick.Layouts + +import FontAwesome + +import "../components" as MoneroComponents ColumnLayout { id: seedListItem diff --git a/wizard/WizardAskPassword.qml b/wizard/WizardAskPassword.qml index ca9c94c3d0..72a6013c06 100644 --- a/wizard/WizardAskPassword.qml +++ b/wizard/WizardAskPassword.qml @@ -26,10 +26,11 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 -import FontAwesome 1.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls + +import FontAwesome import "../js/Wizard.js" as Wizard import "../components" as MoneroComponents diff --git a/wizard/WizardController.qml b/wizard/WizardController.qml index 6e722d893c..a9f32bc13c 100644 --- a/wizard/WizardController.qml +++ b/wizard/WizardController.qml @@ -26,14 +26,11 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQml 2.0 -import QtQuick 2.9 -import QtQuick.Controls 2.0 -import QtQuick.Controls 1.4 -import QtGraphicalEffects 1.0 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.2 -import QtQuick.Dialogs 1.2 +import QtQml +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs import moneroComponents.Wallet 1.0 import "../js/Wizard.js" as Wizard @@ -169,8 +166,13 @@ Rectangle { wizardController.restart(); if (currentView) { - stackView.replace(currentView) - // Calls when view is opened + if (stackView.currentItem !== currentView) { + if (stackView.depth > 0) { + stackView.replace(currentView); + } else { + stackView.push(currentView); + } + } if (typeof currentView.onPageCompleted === "function") { currentView.onPageCompleted(previousView); } @@ -292,24 +294,23 @@ Rectangle { anchors.fill: parent clip: true - delegate: StackViewDelegate { - pushTransition: StackViewTransition { - PropertyAnimation { - target: enterItem - property: "x" - from: stackView.backTransition ? -target.width : target.width - to: 0 - duration: 300 - easing.type: Easing.OutCubic - } - PropertyAnimation { - target: exitItem - property: "x" - from: 0 - to: stackView.backTransition ? target.width : -target.width - duration: 300 - easing.type: Easing.OutCubic - } + replaceEnter: Transition { + PropertyAnimation { + property: "x" + from: stackView.backTransition ? -stackView.width : stackView.width + to: 0 + duration: 300 + easing.type: Easing.OutCubic + } + } + + replaceExit: Transition { + PropertyAnimation { + property: "x" + from: 0 + to: stackView.backTransition ? stackView.width : -stackView.width + duration: 300 + easing.type: Easing.OutCubic } } } @@ -320,12 +321,11 @@ Rectangle { FileDialog { id: fileDialog title: qsTr("Please choose a file") + translationManager.emptyString - folder: "file://" + appWindow.accountsDir + currentFolder: "file://" + appWindow.accountsDir nameFilters: [ "Wallet files (*.keys)"] - sidebarVisible: false onAccepted: { - var keysPath = walletManager.urlToLocalPath(fileDialog.fileUrl) + var keysPath = walletManager.urlToLocalPath(fileDialog.selectedFile) persistentSettings.nettype = oshelper.getNetworkTypeFromFile(keysPath); wizardController.openWalletFile(keysPath); } diff --git a/wizard/WizardCreateDevice1.qml b/wizard/WizardCreateDevice1.qml index 268091c3e2..00301e02a7 100644 --- a/wizard/WizardCreateDevice1.qml +++ b/wizard/WizardCreateDevice1.qml @@ -26,10 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Dialogs 1.2 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Dialogs +import QtQuick.Layouts +import QtQuick.Controls import moneroComponents.Wallet 1.0 import "../js/Wizard.js" as Wizard @@ -214,8 +214,8 @@ Rectangle { labelFontSize: 14 placeholderFontSize: 16 placeholderText: qsTr("Restore height") + translationManager.emptyString - validator: RegExpValidator { - regExp: /^(\d+|\d{4}-\d{2}-\d{2})$/ + validator: RegularExpressionValidator { + regularExpression: /^(\d+|\d{4}-\d{2}-\d{2})$/ } text: "1" } @@ -235,7 +235,7 @@ Rectangle { labelFontSize: 14 placeholderText: ":" placeholderFontSize: 16 - validator: RegExpValidator { regExp: /(\d+):(\d+)?$/ } + validator: RegularExpressionValidator { regularExpression: /(\d+):(\d+)?$/ } } } diff --git a/wizard/WizardCreateWallet1.qml b/wizard/WizardCreateWallet1.qml index 40ebf4d690..9d5d0cfa23 100644 --- a/wizard/WizardCreateWallet1.qml +++ b/wizard/WizardCreateWallet1.qml @@ -26,10 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Dialogs 1.2 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Dialogs +import QtQuick.Layouts +import QtQuick.Controls import "../js/Wizard.js" as Wizard import "../js/Utils.js" as Utils diff --git a/wizard/WizardCreateWallet2.qml b/wizard/WizardCreateWallet2.qml index f52fd20b6b..2cb113992d 100644 --- a/wizard/WizardCreateWallet2.qml +++ b/wizard/WizardCreateWallet2.qml @@ -26,10 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Dialogs 1.2 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Dialogs +import QtQuick.Layouts +import QtQuick.Controls import moneroComponents.Clipboard 1.0 import "../js/Wizard.js" as Wizard diff --git a/wizard/WizardCreateWallet3.qml b/wizard/WizardCreateWallet3.qml index 0ea16290c8..c13bed9ee0 100644 --- a/wizard/WizardCreateWallet3.qml +++ b/wizard/WizardCreateWallet3.qml @@ -26,9 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import "../components" as MoneroComponents diff --git a/wizard/WizardCreateWallet4.qml b/wizard/WizardCreateWallet4.qml index a42af60b1f..8e5a6077f4 100644 --- a/wizard/WizardCreateWallet4.qml +++ b/wizard/WizardCreateWallet4.qml @@ -26,9 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import "../components" as MoneroComponents diff --git a/wizard/WizardCreateWallet5.qml b/wizard/WizardCreateWallet5.qml index e6cb9e9336..7c62a5b221 100644 --- a/wizard/WizardCreateWallet5.qml +++ b/wizard/WizardCreateWallet5.qml @@ -26,9 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import "../js/Wizard.js" as Wizard import "../components" as MoneroComponents diff --git a/wizard/WizardDaemonSettings.qml b/wizard/WizardDaemonSettings.qml index 7669db7b6a..80aa2485b8 100644 --- a/wizard/WizardDaemonSettings.qml +++ b/wizard/WizardDaemonSettings.qml @@ -26,10 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Dialogs 1.2 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Dialogs +import QtQuick.Layouts +import QtQuick.Controls import "../js/Wizard.js" as Wizard import "../components" as MoneroComponents @@ -95,7 +95,7 @@ ColumnLayout { text: qsTr("Browse") + translationManager.emptyString onClicked: { if(persistentSettings.blockchainDataDir != ""); - blockchainFileDialog.folder = "file://" + persistentSettings.blockchainDataDir; + blockchainFileDialog.currentFolder = "file://" + persistentSettings.blockchainDataDir; blockchainFileDialog.open(); blockchainFolder.focus = true; } diff --git a/wizard/WizardHeader.qml b/wizard/WizardHeader.qml index 3febfa49ff..2169f3c53e 100644 --- a/wizard/WizardHeader.qml +++ b/wizard/WizardHeader.qml @@ -30,9 +30,9 @@ import "../js/Wizard.js" as Wizard import "../components" import "../components" as MoneroComponents -import QtQuick 2.9 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls ColumnLayout { diff --git a/wizard/WizardHome.qml b/wizard/WizardHome.qml index d55ddd74cd..c0202eaedd 100644 --- a/wizard/WizardHome.qml +++ b/wizard/WizardHome.qml @@ -26,10 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Dialogs 1.2 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Dialogs +import QtQuick.Layouts +import QtQuick.Controls import moneroComponents.NetworkType 1.0 import "../components" as MoneroComponents @@ -41,6 +41,7 @@ Rectangle { property alias createWalletButton: createWalletButton property alias openWalletButton: openWalletButton property alias restoreWalletButton: restoreWalletButton + property alias changeWalletModeButton: changeWalletModeButton property string viewName: "wizardHome" ColumnLayout { @@ -167,6 +168,7 @@ Rectangle { spacing: 20 MoneroComponents.StandardButton { + id: changeWalletModeButton small: true text: qsTr("Change wallet mode") + translationManager.emptyString @@ -248,7 +250,6 @@ Rectangle { kdfRoundsText.kdfWarningShown = true; confirmationDialog.title = qsTr("Warning") + translationManager.emptyString; confirmationDialog.text = qsTr("You have selected a non-standard number of KDF rounds.\n\nThis value is set globally and is not stored in the wallet file. It must be remembered and re-entered every time this wallet is opened; otherwise, the wallet will not open due to the password being incorrect.\n\nHigher values make opening and saving the wallet significantly slower.") + translationManager.emptyString; - confirmationDialog.icon = StandardIcon.Warning; confirmationDialog.onAcceptedCallback = null; confirmationDialog.onRejectedCallback = function() { kdfRoundsText.text = "1"; diff --git a/wizard/WizardLanguage.qml b/wizard/WizardLanguage.qml index 0c5b980e9b..d955957e53 100644 --- a/wizard/WizardLanguage.qml +++ b/wizard/WizardLanguage.qml @@ -26,9 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import "../components" import "../components" as MoneroComponents @@ -237,8 +237,4 @@ Rectangle { Timer { id: versionTimer } - - function onPageCompleted() { - persistentSettings.setWritable(false); - } } diff --git a/wizard/WizardMenuItem.qml b/wizard/WizardMenuItem.qml index 5e9964b56f..d724406857 100644 --- a/wizard/WizardMenuItem.qml +++ b/wizard/WizardMenuItem.qml @@ -26,11 +26,11 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Dialogs 1.2 -import QtQuick.Layouts 1.2 -import QtGraphicalEffects 1.0 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Effects +import QtQuick.Dialogs +import QtQuick.Layouts +import QtQuick.Controls import "../components" as MoneroComponents @@ -60,22 +60,22 @@ RowLayout { Image { id: icon - visible: !rowlayout.checkbox && (!isOpenGL || MoneroComponents.Style.blackTheme) + visible: !rowlayout.checkbox && (GraphicsInfo.api === GraphicsInfo.Software || MoneroComponents.Style.blackTheme) anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter source: "" } - DropShadow { - visible: !rowlayout.checkbox && (isOpenGL && !MoneroComponents.Style.blackTheme) + MultiEffect { + visible: !rowlayout.checkbox && (GraphicsInfo.api !== GraphicsInfo.Software && !MoneroComponents.Style.blackTheme) anchors.fill: icon - horizontalOffset: 3 - verticalOffset: 3 - radius: 10.0 - samples: 15 - color: "#1E000000" source: icon - cached: true + shadowEnabled: true + shadowHorizontalOffset: 3 + shadowVerticalOffset: 3 + shadowBlur: 0.625 + blurMax: 16 + shadowColor: "#1E000000" } MouseArea { diff --git a/wizard/WizardModeBootstrap.qml b/wizard/WizardModeBootstrap.qml index df4d8efb73..e878d38941 100644 --- a/wizard/WizardModeBootstrap.qml +++ b/wizard/WizardModeBootstrap.qml @@ -26,9 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import "../js/Wizard.js" as Wizard import "../components" as MoneroComponents diff --git a/wizard/WizardModeRemoteNodeWarning.qml b/wizard/WizardModeRemoteNodeWarning.qml index e7a947735a..2a6a1ee9a8 100644 --- a/wizard/WizardModeRemoteNodeWarning.qml +++ b/wizard/WizardModeRemoteNodeWarning.qml @@ -26,9 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import "../js/Wizard.js" as Wizard import "../components" as MoneroComponents diff --git a/wizard/WizardModeSelection.qml b/wizard/WizardModeSelection.qml index 050b440f8e..5c3cce07fb 100644 --- a/wizard/WizardModeSelection.qml +++ b/wizard/WizardModeSelection.qml @@ -26,10 +26,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Dialogs 1.2 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Dialogs +import QtQuick.Layouts +import QtQuick.Controls import "../js/Wizard.js" as Wizard import "../components" as MoneroComponents @@ -40,11 +40,14 @@ Rectangle { property alias pageHeight: pageRoot.height property string viewName: "wizardModeSelection1" - property bool portable: persistentSettings.portable + property bool portable: portableSettings.portable + property alias advancedModeButton: advancedModeButton + property alias portableModeButton: portableModeButton property bool simpleModeAvailable: !isTails && appWindow.persistentSettings.nettype == 0 && !isAndroid function applyWalletMode(mode, wizardState) { - if (!persistentSettings.setPortable(portable)) { + persistentSettings.sync(); + if (!portableSettings.setPortable(portable)) { appWindow.showStatusMessage(qsTr("Failed to configure portable mode"), 3); return; } @@ -144,6 +147,7 @@ Rectangle { } WizardMenuItem { + id: advancedModeButton headerText: qsTr("Advanced mode") + translationManager.emptyString bodyText: qsTr("Includes extra features like mining and message verification. The blockchain is downloaded to your computer.") + translationManager.emptyString imageIcon: "qrc:///images/local-node-full.png" @@ -161,6 +165,7 @@ Rectangle { } WizardMenuItem { + id: portableModeButton Layout.topMargin: 20 headerText: qsTr("Portable mode") + translationManager.emptyString bodyText: qsTr("Create portable wallets and use them on any PC. Enable if you installed Monero on a USB stick, an external drive, or any other portable storage medium.") + translationManager.emptyString diff --git a/wizard/WizardNav.qml b/wizard/WizardNav.qml index b5511b3ed1..442f43aa6c 100644 --- a/wizard/WizardNav.qml +++ b/wizard/WizardNav.qml @@ -26,9 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import "../js/Wizard.js" as Wizard import "../components" as MoneroComponents diff --git a/wizard/WizardOpenWallet1.qml b/wizard/WizardOpenWallet1.qml index 8a45523526..f0204a8dc3 100644 --- a/wizard/WizardOpenWallet1.qml +++ b/wizard/WizardOpenWallet1.qml @@ -26,15 +26,14 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Dialogs 1.2 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 -import QtGraphicalEffects 1.0 -import Qt.labs.folderlistmodel 2.1 +import QtQuick +import QtQuick.Effects +import QtQuick.Dialogs +import QtQuick.Layouts +import QtQuick.Controls +import Qt.labs.folderlistmodel import moneroComponents.NetworkType 1.0 import moneroComponents.WalletKeysFilesModel 1.0 -import FontAwesome 1.0 import "../js/Wizard.js" as Wizard import "../components" @@ -230,18 +229,18 @@ Rectangle { else if (networktype === 2) return "qrc:///images/open-wallet-from-file-stagenet.png"; } visible: { - if(!isOpenGL) return true; + if(GraphicsInfo.api === GraphicsInfo.Software) return true; if(MoneroComponents.Style.blackTheme) return true; return false; } } - Colorize { - visible: isOpenGL && !MoneroComponents.Style.blackTheme + MultiEffect { + visible: GraphicsInfo.api !== GraphicsInfo.Software && !MoneroComponents.Style.blackTheme anchors.fill: icon source: icon - lightness: 0.65 // +65% - saturation: 0.0 + brightness: 0.65 + saturation: -1.0 } } diff --git a/wizard/WizardRestoreWallet1.qml b/wizard/WizardRestoreWallet1.qml index 2ee70bd6e3..59ad438b5d 100644 --- a/wizard/WizardRestoreWallet1.qml +++ b/wizard/WizardRestoreWallet1.qml @@ -26,9 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import "../js/Wizard.js" as Wizard import "../js/Utils.js" as Utils @@ -277,8 +277,8 @@ Rectangle { labelFontSize: 14 placeholderFontSize: 16 placeholderText: qsTr("Restore height") + translationManager.emptyString - validator: RegExpValidator { - regExp: /^(\d+|\d{4}-\d{2}-\d{2})$/ + validator: RegularExpressionValidator { + regularExpression: /^(\d+|\d{4}-\d{2}-\d{2})$/ } text: "0" } diff --git a/wizard/WizardRestoreWallet2.qml b/wizard/WizardRestoreWallet2.qml index eb57f8d488..ba67b05d5b 100644 --- a/wizard/WizardRestoreWallet2.qml +++ b/wizard/WizardRestoreWallet2.qml @@ -29,9 +29,9 @@ import "../js/Wizard.js" as Wizard import "../components" as MoneroComponents -import QtQuick 2.9 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls Rectangle { diff --git a/wizard/WizardRestoreWallet3.qml b/wizard/WizardRestoreWallet3.qml index 31fb9ab053..08ccb08d64 100644 --- a/wizard/WizardRestoreWallet3.qml +++ b/wizard/WizardRestoreWallet3.qml @@ -29,9 +29,9 @@ import "../js/Wizard.js" as Wizard import "../components" as MoneroComponents -import QtQuick 2.9 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls Rectangle { diff --git a/wizard/WizardRestoreWallet4.qml b/wizard/WizardRestoreWallet4.qml index d2d129b178..2a88599995 100644 --- a/wizard/WizardRestoreWallet4.qml +++ b/wizard/WizardRestoreWallet4.qml @@ -28,9 +28,9 @@ import "../components" as MoneroComponents -import QtQuick 2.9 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls Rectangle { diff --git a/wizard/WizardSummary.qml b/wizard/WizardSummary.qml index 85ee174a7d..2ebe989b55 100644 --- a/wizard/WizardSummary.qml +++ b/wizard/WizardSummary.qml @@ -26,9 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import moneroComponents.NetworkType 1.0 import "../js/Wizard.js" as Wizard diff --git a/wizard/WizardSummaryItem.qml b/wizard/WizardSummaryItem.qml index d3284b0dd6..f2c539d8ae 100644 --- a/wizard/WizardSummaryItem.qml +++ b/wizard/WizardSummaryItem.qml @@ -26,9 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import "../js/Wizard.js" as Wizard import "../js/Utils.js" as Utils diff --git a/wizard/WizardWalletInput.qml b/wizard/WizardWalletInput.qml index 8afc2902ae..3d6767f6e3 100644 --- a/wizard/WizardWalletInput.qml +++ b/wizard/WizardWalletInput.qml @@ -26,11 +26,12 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import QtQuick 2.9 -import QtQuick.Dialogs 1.2 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.0 -import FontAwesome 1.0 +import QtQuick +import QtQuick.Dialogs +import QtQuick.Layouts +import QtQuick.Controls + +import FontAwesome import "../js/Wizard.js" as Wizard import "../components" @@ -188,7 +189,7 @@ GridLayout { tooltip: qsTr("Browse") + translationManager.emptyString tooltipLeft: true onClicked: { - fileWalletDialog.folder = walletManager.localPathToUrl(walletLocation.text) + fileWalletDialog.currentFolder = walletManager.localPathToUrl(walletLocation.text) fileWalletDialog.open() walletLocation.focus = true } @@ -231,13 +232,11 @@ GridLayout { } } - FileDialog { + FolderDialog { id: fileWalletDialog - selectMultiple: false - selectFolder: true title: qsTr("Please choose a directory") + translationManager.emptyString onAccepted: { - walletLocation.text = walletManager.urlToLocalPath(fileWalletDialog.folder); + walletLocation.text = walletManager.urlToLocalPath(fileWalletDialog.selectedFolder); fileWalletDialog.visible = false; walletName.error = !walletName.verify(); }