mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-23 11:28:49 +03:00
Merge branch 'Nheko-Reborn:master' into master
This commit is contained in:
commit
dee0115daf
231 changed files with 5403 additions and 5364 deletions
|
@ -6,7 +6,7 @@ brew "clang-format"
|
||||||
brew "cmake"
|
brew "cmake"
|
||||||
brew "ninja"
|
brew "ninja"
|
||||||
brew "openssl"
|
brew "openssl"
|
||||||
brew "qt5"
|
brew "qt6"
|
||||||
brew "nlohmann_json"
|
brew "nlohmann_json"
|
||||||
brew "gstreamer"
|
brew "gstreamer"
|
||||||
brew "qtkeychain"
|
brew "qtkeychain"
|
||||||
|
|
|
@ -6,27 +6,32 @@ set -ue
|
||||||
#TAG=$(git tag -l --points-at HEAD)
|
#TAG=$(git tag -l --points-at HEAD)
|
||||||
|
|
||||||
# Add Qt binaries to path
|
# Add Qt binaries to path
|
||||||
PATH="$(brew --prefix qt5):${PATH}"
|
PATH="$(brew --prefix qt6)/bin/:${PATH}"
|
||||||
export PATH
|
export PATH
|
||||||
|
|
||||||
CMAKE_PREFIX_PATH="$(brew --prefix qt5)"
|
CMAKE_PREFIX_PATH="$(brew --prefix qt6)"
|
||||||
export CMAKE_PREFIX_PATH
|
export CMAKE_PREFIX_PATH
|
||||||
|
|
||||||
cmake -GNinja -S. -Bbuild \
|
cmake -GNinja -S. -Bbuild \
|
||||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||||
-DCMAKE_INSTALL_PREFIX=.deps/usr \
|
-DCMAKE_INSTALL_PREFIX="nheko.temp" \
|
||||||
-DHUNTER_ROOT="../.hunter" \
|
-DHUNTER_ROOT="../.hunter" \
|
||||||
-DHUNTER_ENABLED=ON -DBUILD_SHARED_LIBS=OFF \
|
-DHUNTER_ENABLED=ON -DBUILD_SHARED_LIBS=OFF \
|
||||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo -DHUNTER_CONFIGURATION_TYPES=RelWithDebInfo \
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo -DHUNTER_CONFIGURATION_TYPES=RelWithDebInfo \
|
||||||
-DUSE_BUNDLED_OPENSSL=ON \
|
-DUSE_BUNDLED_OPENSSL=ON \
|
||||||
-DCI_BUILD=ON
|
-DCI_BUILD=ON
|
||||||
cmake --build build
|
cmake --build build
|
||||||
|
cmake --install build
|
||||||
( cd build
|
( cd build
|
||||||
git clone https://github.com/Nheko-Reborn/qt-jdenticon.git
|
git clone https://github.com/Nheko-Reborn/qt-jdenticon.git
|
||||||
( cd qt-jdenticon
|
( cd qt-jdenticon
|
||||||
qmake
|
qmake
|
||||||
make -j 4
|
make -j 4
|
||||||
cp libqtjdenticon.dylib ../nheko.app/Contents/MacOS
|
cp libqtjdenticon.dylib ../../nheko.temp/nheko.app/Contents/MacOS
|
||||||
)
|
)
|
||||||
"$(brew --prefix qt5)/bin/macdeployqt" nheko.app -always-overwrite -qmldir=../resources/qml/
|
# "$(brew --prefix qt6)/bin/macdeployqt" nheko.app -always-overwrite -qmldir=../resources/qml/
|
||||||
|
# # workaround for https://bugreports.qt.io/browse/QTBUG-100686
|
||||||
|
# cp "$(brew --prefix brotli)/lib/libbrotlicommon.1.dylib" nheko.app/Contents/Frameworks/libbrotlicommon.1.dylib
|
||||||
)
|
)
|
||||||
|
|
||||||
|
mv nheko.temp/nheko.app nheko.app
|
||||||
|
|
|
@ -6,7 +6,7 @@ set -u
|
||||||
# https://forum.qt.io/topic/96652/how-to-notarize-qt-application-on-macos/18
|
# https://forum.qt.io/topic/96652/how-to-notarize-qt-application-on-macos/18
|
||||||
|
|
||||||
# Add Qt binaries to path
|
# Add Qt binaries to path
|
||||||
PATH="/usr/local/opt/qt@5/bin/:${PATH}"
|
PATH="/usr/local/opt/qt@6/bin/:${PATH}"
|
||||||
export PATH
|
export PATH
|
||||||
|
|
||||||
security unlock-keychain -p "${RUNNER_USER_PW}" login.keychain
|
security unlock-keychain -p "${RUNNER_USER_PW}" login.keychain
|
||||||
|
@ -20,25 +20,23 @@ if [ -n "${CI_PIPELINE_TRIGGERED:-}" ] && [ "${TRIGGERED_BY:-}" = "cirrus" ]; th
|
||||||
unzip binaries.zip
|
unzip binaries.zip
|
||||||
# we zip 'build/nheko.app' in cirrus ci, cirrus itself puts it in a 'build' directory
|
# we zip 'build/nheko.app' in cirrus ci, cirrus itself puts it in a 'build' directory
|
||||||
# so move it to the right place for the rest of the process.
|
# so move it to the right place for the rest of the process.
|
||||||
( cd build || exit
|
unzip nheko.zip
|
||||||
unzip nheko.zip
|
|
||||||
)
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -d "build/nheko.app" ]; then
|
if [ ! -d "nheko.app" ]; then
|
||||||
echo "nheko.app is missing, you did something wrong!"
|
echo "nheko.app is missing, you did something wrong!"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "[INFO] Signing app contents"
|
echo "[INFO] Signing app contents"
|
||||||
find "build/nheko.app/Contents"|while read -r fname; do
|
find "nheko.app/Contents"|while read -r fname; do
|
||||||
if [ -f "$fname" ]; then
|
if [ -f "$fname" ]; then
|
||||||
echo "[INFO] Signing $fname"
|
echo "[INFO] Signing $fname"
|
||||||
codesign --force --timestamp --options=runtime --sign "${APPLE_DEV_IDENTITY}" "$fname"
|
codesign --force --timestamp --options=runtime --sign "${APPLE_DEV_IDENTITY}" "$fname"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
codesign --force --timestamp --options=runtime --sign "${APPLE_DEV_IDENTITY}" "build/nheko.app"
|
codesign --force --timestamp --options=runtime --sign "${APPLE_DEV_IDENTITY}" "nheko.app"
|
||||||
|
|
||||||
NOTARIZE_SUBMIT_LOG=$(mktemp /tmp/notarize-submit.XXXXXX)
|
NOTARIZE_SUBMIT_LOG=$(mktemp /tmp/notarize-submit.XXXXXX)
|
||||||
NOTARIZE_STATUS_LOG=$(mktemp /tmp/notarize-status.XXXXXX)
|
NOTARIZE_STATUS_LOG=$(mktemp /tmp/notarize-status.XXXXXX)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"compression-level": 9,
|
"compression-level": 9,
|
||||||
"contents": [
|
"contents": [
|
||||||
{
|
{
|
||||||
"path": "./build/Nheko.app",
|
"path": "./nheko.app",
|
||||||
"type": "file",
|
"type": "file",
|
||||||
"x": 140,
|
"x": 140,
|
||||||
"y": 120
|
"y": 120
|
||||||
|
|
|
@ -13,9 +13,9 @@ task:
|
||||||
- export PATH="$(brew --prefix qt5)/bin/:${PATH}"
|
- export PATH="$(brew --prefix qt5)/bin/:${PATH}"
|
||||||
- ./.ci/macos/build.sh
|
- ./.ci/macos/build.sh
|
||||||
zip_script:
|
zip_script:
|
||||||
- ditto -c -k --sequesterRsrc --keepParent build/nheko.app build/nheko.zip
|
- ditto -c -k --sequesterRsrc --keepParent nheko.app nheko.zip
|
||||||
gitlab_script:
|
gitlab_script:
|
||||||
- >
|
- >
|
||||||
[ "${CIRRUS_BRANCH}" == "master" ] && curl -X POST --fail -F token="${GITLAB_TRIGGER_TOKEN}" -F ref="${CIRRUS_BRANCH}" -F "variables[TRIGGER_BUILD_ID]=${CIRRUS_BUILD_ID}" -F "variables[TRIGGERED_BY]=cirrus" "https://nheko.im/api/v4/projects/2/trigger/pipeline" || true
|
[ "${CIRRUS_BRANCH}" == "master" ] && curl -X POST --fail -F token="${GITLAB_TRIGGER_TOKEN}" -F ref="${CIRRUS_BRANCH}" -F "variables[TRIGGER_BUILD_ID]=${CIRRUS_BUILD_ID}" -F "variables[TRIGGERED_BY]=cirrus" "https://nheko.im/api/v4/projects/2/trigger/pipeline" || true
|
||||||
binaries_artifacts:
|
binaries_artifacts:
|
||||||
path: build/nheko.zip
|
path: nheko.zip
|
||||||
|
|
|
@ -18,14 +18,14 @@ build-clazy:
|
||||||
TRAVIS_OS_NAME: linux
|
TRAVIS_OS_NAME: linux
|
||||||
before_script:
|
before_script:
|
||||||
- echo -e "\e[0Ksection_start:`date +%s`:install_deps[collapsed=true]\r\e[0K\e[1m\e[95mInstalling apk dependencies"
|
- echo -e "\e[0Ksection_start:`date +%s`:install_deps[collapsed=true]\r\e[0K\e[1m\e[95mInstalling apk dependencies"
|
||||||
- apk add asciidoctor cmake cmark-dev gst-plugins-bad-dev gst-plugins-base-dev gstreamer-dev lmdb-dev lmdbxx nlohmann-json olm-dev openssl-dev qt5-qtbase-dev qt5-qtdeclarative-dev qt5-qtmultimedia-dev qt5-qtquickcontrols2-dev qt5-qtsvg-dev qt5-qttools-dev qtkeychain-dev samurai spdlog-dev xcb-util-wm-dev zlib-dev ccache curl-dev libevent-dev meson clazy clang16 gcc musl-dev git re2-dev
|
- apk add asciidoctor cmake cmark-dev gst-plugins-bad-dev gst-plugins-base-dev gstreamer-dev lmdb-dev lmdbxx nlohmann-json olm-dev openssl-dev qt6-qtbase-dev qt6-qtdeclarative-dev qt6-qtmultimedia-dev qt6-qtsvg-dev qt6-qttools-dev samurai spdlog-dev xcb-util-wm-dev zlib-dev ccache curl-dev libevent-dev meson clazy clang16 gcc musl-dev git re2-dev libsecret-dev
|
||||||
- echo -e "\e[0Ksection_end:`date +%s`:install_deps\r\e[0K"
|
- echo -e "\e[0Ksection_end:`date +%s`:install_deps\r\e[0K"
|
||||||
script:
|
script:
|
||||||
- export PATH="/usr/lib/ccache:${PATH}"
|
- export PATH="/usr/lib/ccache:${PATH}"
|
||||||
- export CMAKE_BUILD_PARALLEL_LEVEL=$(cat /proc/cpuinfo | awk '/^processor/{print $3}' | wc -l)
|
- export CMAKE_BUILD_PARALLEL_LEVEL=$(cat /proc/cpuinfo | awk '/^processor/{print $3}' | wc -l)
|
||||||
- cmake -GNinja -H. -Bbuild
|
- cmake -GNinja -H. -Bbuild
|
||||||
-DCMAKE_INSTALL_PREFIX=.deps/usr
|
-DCMAKE_INSTALL_PREFIX=.deps/usr
|
||||||
-DHUNTER_ENABLED=OFF -DBUILD_SHARED_LIBS=OFF -DUSE_BUNDLED_OPENSSL=ON -DUSE_BUNDLED_MTXCLIENT=ON -DUSE_BUNDLED_COEURL=ON -DUSE_BUNDLED_OLM=ON
|
-DHUNTER_ENABLED=OFF -DBUILD_SHARED_LIBS=OFF -DUSE_BUNDLED_OPENSSL=ON -DUSE_BUNDLED_MTXCLIENT=ON -DUSE_BUNDLED_COEURL=ON -DUSE_BUNDLED_OLM=ON -DUSE_BUNDLED_QTKEYCHAIN=ON
|
||||||
-DVOIP=OFF
|
-DVOIP=OFF
|
||||||
-DCMAKE_BUILD_TYPE=Release
|
-DCMAKE_BUILD_TYPE=Release
|
||||||
-DCI_BUILD=ON -DFETCHCONTENT_QUIET=OFF -DCMAKE_CXX_COMPILER=clazy
|
-DCI_BUILD=ON -DFETCHCONTENT_QUIET=OFF -DCMAKE_CXX_COMPILER=clazy
|
||||||
|
@ -37,7 +37,8 @@ build-clazy:
|
||||||
paths:
|
paths:
|
||||||
- .ccache
|
- .ccache
|
||||||
|
|
||||||
build-gcc11:
|
# disabled until I find a qt6.5 ppa
|
||||||
|
.build-gcc11:
|
||||||
stage: build
|
stage: build
|
||||||
image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/ubuntu:22.04
|
image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/ubuntu:22.04
|
||||||
tags: [docker]
|
tags: [docker]
|
||||||
|
@ -50,7 +51,7 @@ build-gcc11:
|
||||||
libssl-dev libqt5multimedia5-plugins libqt5multimediagsttools5 libqt5multimediaquick5 libqt5svg5-dev
|
libssl-dev libqt5multimedia5-plugins libqt5multimediagsttools5 libqt5multimediaquick5 libqt5svg5-dev
|
||||||
qtmultimedia5-dev qtquickcontrols2-5-dev qttools5-dev qttools5-dev-tools qtdeclarative5-dev
|
qtmultimedia5-dev qtquickcontrols2-5-dev qttools5-dev qttools5-dev-tools qtdeclarative5-dev
|
||||||
qml-module-qtmultimedia qml-module-qtquick-controls2 qml-module-qtquick-layouts qml-module-qt-labs-platform
|
qml-module-qtmultimedia qml-module-qtquick-controls2 qml-module-qtquick-layouts qml-module-qt-labs-platform
|
||||||
qt5keychain-dev ccache clazy libcurl4-openssl-dev libevent-dev libspdlog-dev git nlohmann-json3-dev libcmark-dev asciidoc time # libolm-dev
|
qt5keychain-dev ccache libcurl4-openssl-dev libevent-dev libspdlog-dev git nlohmann-json3-dev libcmark-dev asciidoc time # libolm-dev
|
||||||
# need recommended deps for wget
|
# need recommended deps for wget
|
||||||
- apt-get -y install wget
|
- apt-get -y install wget
|
||||||
- /usr/sbin/update-ccache-symlinks
|
- /usr/sbin/update-ccache-symlinks
|
||||||
|
@ -107,16 +108,15 @@ build-tw:
|
||||||
"zlib-devel"
|
"zlib-devel"
|
||||||
"libQt5PlatformHeaders-devel"
|
"libQt5PlatformHeaders-devel"
|
||||||
"cmake(re2)"
|
"cmake(re2)"
|
||||||
"cmake(Qt5Concurrent)"
|
"cmake(Qt6Core)"
|
||||||
"cmake(Qt5Core)"
|
"cmake(Qt6DBus)"
|
||||||
"cmake(Qt5DBus)"
|
"cmake(Qt6Keychain)"
|
||||||
"cmake(Qt5Keychain)"
|
"cmake(Qt6LinguistTools)"
|
||||||
"cmake(Qt5LinguistTools)"
|
"cmake(Qt6Multimedia)"
|
||||||
"cmake(Qt5Multimedia)"
|
"cmake(Qt6QuickControls2)"
|
||||||
"cmake(Qt5Network)"
|
"cmake(Qt6Svg)"
|
||||||
"cmake(Qt5QuickControls2)"
|
"cmake(Qt6Widgets)"
|
||||||
"cmake(Qt5Svg)"
|
"cmake(Qt6Gui)"
|
||||||
"cmake(Qt5Widgets)"
|
|
||||||
"pkgconfig(libcurl)"
|
"pkgconfig(libcurl)"
|
||||||
"pkgconfig(libevent)"
|
"pkgconfig(libevent)"
|
||||||
"pkgconfig(gstreamer-webrtc-1.0)"
|
"pkgconfig(gstreamer-webrtc-1.0)"
|
||||||
|
@ -155,7 +155,7 @@ build-macos:
|
||||||
- if : '$CI_PIPELINE_TRIGGERED == null'
|
- if : '$CI_PIPELINE_TRIGGERED == null'
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- build/nheko.app # not putting this in 'artifacts' subdir because we don't want to put it on releases
|
- nheko.app # not putting this in 'artifacts' subdir because we don't want to put it on releases
|
||||||
name: nheko-${CI_COMMIT_SHORT_SHA}-macos-app
|
name: nheko-${CI_COMMIT_SHORT_SHA}-macos-app
|
||||||
expose_as: 'macos-app'
|
expose_as: 'macos-app'
|
||||||
public: false
|
public: false
|
||||||
|
@ -200,7 +200,10 @@ build-flatpak:
|
||||||
- docker-${ARCH}
|
- docker-${ARCH}
|
||||||
parallel:
|
parallel:
|
||||||
matrix:
|
matrix:
|
||||||
- ARCH: [amd64, arm64]
|
- ARCH: amd64
|
||||||
|
JOBS: 0
|
||||||
|
- ARCH: arm64
|
||||||
|
JOBS: 3
|
||||||
before_script:
|
before_script:
|
||||||
- echo -e "\e[0Ksection_start:`date +%s`:install_deps[collapsed=true]\r\e[0K\e[1m\e[95mInstalling apt dependencies"
|
- echo -e "\e[0Ksection_start:`date +%s`:install_deps[collapsed=true]\r\e[0K\e[1m\e[95mInstalling apt dependencies"
|
||||||
- apt-get update && apt-get -y install flatpak-builder git python3 curl python3-aiohttp python3-tenacity gir1.2-ostree-1.0
|
- apt-get update && apt-get -y install flatpak-builder git python3 curl python3-aiohttp python3-tenacity gir1.2-ostree-1.0
|
||||||
|
@ -213,9 +216,9 @@ build-flatpak:
|
||||||
- mkdir -p build-flatpak
|
- mkdir -p build-flatpak
|
||||||
- cd build-flatpak
|
- cd build-flatpak
|
||||||
- echo -e "\e[0Ksection_start:`date +%s`:build_flatpak[collapsed=true]\r\e[0K\e[1m\e[95mBuilding flatpak"
|
- echo -e "\e[0Ksection_start:`date +%s`:build_flatpak[collapsed=true]\r\e[0K\e[1m\e[95mBuilding flatpak"
|
||||||
- flatpak-builder --install-deps-from=flathub --user --disable-rofiles-fuse --ccache --repo=repo --default-branch=${CI_COMMIT_REF_NAME//\//_} --subject="Build of Nheko ${VERSION} `date` for ${ARCH}" app ../io.github.NhekoReborn.Nheko.yaml
|
- flatpak-builder --install-deps-from=flathub --user --disable-rofiles-fuse --ccache --repo=repo --default-branch=${CI_COMMIT_REF_NAME//\//_} --subject="Build of Nheko ${VERSION} `date` for ${ARCH}" app ../im.nheko.Nheko.yaml --jobs=$JOBS
|
||||||
- echo -e "\e[0Ksection_end:`date +%s`:build_flatpak\r\e[0K"
|
- echo -e "\e[0Ksection_end:`date +%s`:build_flatpak\r\e[0K"
|
||||||
- flatpak build-bundle repo nheko-${ARCH}.flatpak io.github.NhekoReborn.Nheko ${CI_COMMIT_REF_NAME//\//_}
|
- flatpak build-bundle repo nheko-${ARCH}.flatpak im.nheko.Nheko ${CI_COMMIT_REF_NAME//\//_}
|
||||||
after_script:
|
after_script:
|
||||||
- echo -e "\e[0Ksection_start:`date +%s`:upload_flatpak[collapsed=true]\r\e[0K\e[1m\e[95mUploading flatpak"
|
- echo -e "\e[0Ksection_start:`date +%s`:upload_flatpak[collapsed=true]\r\e[0K\e[1m\e[95mUploading flatpak"
|
||||||
- bash ./.ci/upload-nightly-gitlab.sh build-flatpak/nheko-${ARCH}.flatpak
|
- bash ./.ci/upload-nightly-gitlab.sh build-flatpak/nheko-${ARCH}.flatpak
|
||||||
|
@ -233,7 +236,8 @@ build-flatpak:
|
||||||
paths: ['build-flatpak/nheko-${ARCH}.flatpak']
|
paths: ['build-flatpak/nheko-${ARCH}.flatpak']
|
||||||
name: flatpak-${CI_COMMIT_REF_NAME}-${VERSION}-${ARCH}
|
name: flatpak-${CI_COMMIT_REF_NAME}-${VERSION}-${ARCH}
|
||||||
|
|
||||||
appimage-amd64:
|
# disabled until I find a qt6.5 ppa for Ubuntu
|
||||||
|
.appimage-amd64:
|
||||||
stage: build
|
stage: build
|
||||||
image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/ubuntu:22.04
|
image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/ubuntu:22.04
|
||||||
tags: [docker]
|
tags: [docker]
|
||||||
|
@ -249,7 +253,7 @@ appimage-amd64:
|
||||||
libssl-dev libqt5multimedia5-plugins libqt5multimediagsttools5 libqt5multimediaquick5 libqt5svg5-dev
|
libssl-dev libqt5multimedia5-plugins libqt5multimediagsttools5 libqt5multimediaquick5 libqt5svg5-dev
|
||||||
qtmultimedia5-dev qtquickcontrols2-5-dev qttools5-dev qttools5-dev-tools qtdeclarative5-dev
|
qtmultimedia5-dev qtquickcontrols2-5-dev qttools5-dev qttools5-dev-tools qtdeclarative5-dev
|
||||||
qml-module-qtmultimedia qml-module-qtquick-controls2 qml-module-qtquick-layouts qml-module-qt-labs-platform
|
qml-module-qtmultimedia qml-module-qtquick-controls2 qml-module-qtquick-layouts qml-module-qt-labs-platform
|
||||||
qt5keychain-dev ccache clazy libcurl4-openssl-dev libevent-dev libspdlog-dev nlohmann-json3-dev libcmark-dev asciidoc libre2-dev libgtest-dev libgl1-mesa-dev qml-module-qtquick-particles2
|
qt5keychain-dev ccache libcurl4-openssl-dev libevent-dev libspdlog-dev nlohmann-json3-dev libcmark-dev asciidoc libre2-dev libgtest-dev libgl1-mesa-dev qml-module-qtquick-particles2
|
||||||
|
|
||||||
# Installing the packages needed to build AppImage
|
# Installing the packages needed to build AppImage
|
||||||
- apt-get -yq install breeze-icon-theme desktop-file-utils elfutils fakeroot file gnupg2 gtk-update-icon-cache libgdk-pixbuf2.0-dev libgdk-pixbuf2.0-0 libglib2.0-bin librsvg2-dev libyaml-dev strace zsync squashfs-tools
|
- apt-get -yq install breeze-icon-theme desktop-file-utils elfutils fakeroot file gnupg2 gtk-update-icon-cache libgdk-pixbuf2.0-dev libgdk-pixbuf2.0-0 libglib2.0-bin librsvg2-dev libyaml-dev strace zsync squashfs-tools
|
||||||
|
@ -325,7 +329,7 @@ github-release:
|
||||||
rules:
|
rules:
|
||||||
- if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'
|
- if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'
|
||||||
dependencies:
|
dependencies:
|
||||||
- appimage-amd64
|
#- appimage-amd64 <- disabled because of missing packages
|
||||||
- build-flatpak
|
- build-flatpak
|
||||||
- codesign-macos
|
- codesign-macos
|
||||||
before_script:
|
before_script:
|
||||||
|
|
7
.qmlformat.ini
Normal file
7
.qmlformat.ini
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[General]
|
||||||
|
FunctionsSpacing=
|
||||||
|
IndentWidth=4
|
||||||
|
NewlineType=native
|
||||||
|
NormalizeOrder=true
|
||||||
|
ObjectsSpacing=
|
||||||
|
UseTabs=false
|
250
CMakeLists.txt
250
CMakeLists.txt
|
@ -1,4 +1,4 @@
|
||||||
cmake_minimum_required(VERSION 3.13)
|
cmake_minimum_required(VERSION 3.13..3.21)
|
||||||
|
|
||||||
option(APPVEYOR_BUILD "Build on appveyor" OFF)
|
option(APPVEYOR_BUILD "Build on appveyor" OFF)
|
||||||
option(CI_BUILD "Set when building in CI. Enables -Werror where possible" OFF)
|
option(CI_BUILD "Set when building in CI. Enables -Werror where possible" OFF)
|
||||||
|
@ -57,7 +57,7 @@ option(USE_BUNDLED_OPENSSL "Use the bundled version of OpenSSL." OFF)
|
||||||
option(USE_BUNDLED_MTXCLIENT "Use the bundled version of the Matrix Client library." ${HUNTER_ENABLED})
|
option(USE_BUNDLED_MTXCLIENT "Use the bundled version of the Matrix Client library." ${HUNTER_ENABLED})
|
||||||
option(USE_BUNDLED_LMDB "Use the bundled version of lmdb." ${HUNTER_ENABLED})
|
option(USE_BUNDLED_LMDB "Use the bundled version of lmdb." ${HUNTER_ENABLED})
|
||||||
option(USE_BUNDLED_LMDBXX "Use the bundled version of lmdb++." ${HUNTER_ENABLED})
|
option(USE_BUNDLED_LMDBXX "Use the bundled version of lmdb++." ${HUNTER_ENABLED})
|
||||||
option(USE_BUNDLED_QTKEYCHAIN "Use the bundled version of Qt5Keychain." ${HUNTER_ENABLED})
|
option(USE_BUNDLED_QTKEYCHAIN "Use the bundled version of Qt6Keychain." ${HUNTER_ENABLED})
|
||||||
option(USE_BUNDLED_COEURL "Use a bundled version of the Curl wrapper"
|
option(USE_BUNDLED_COEURL "Use a bundled version of the Curl wrapper"
|
||||||
${HUNTER_ENABLED})
|
${HUNTER_ENABLED})
|
||||||
option(USE_BUNDLED_LIBEVENT "Use the bundled version of libevent." ${HUNTER_ENABLED})
|
option(USE_BUNDLED_LIBEVENT "Use the bundled version of libevent." ${HUNTER_ENABLED})
|
||||||
|
@ -72,7 +72,11 @@ if (APPLE OR WIN32)
|
||||||
set(VOIP_DEFAULT OFF)
|
set(VOIP_DEFAULT OFF)
|
||||||
endif()
|
endif()
|
||||||
option(VOIP "Whether to enable voip support. Disable this, if you don't have gstreamer." ${VOIP_DEFAULT})
|
option(VOIP "Whether to enable voip support. Disable this, if you don't have gstreamer." ${VOIP_DEFAULT})
|
||||||
cmake_dependent_option(SCREENSHARE_X11 "Whether to enable screenshare support on X11." ON "VOIP" OFF)
|
set(X11_DEFAULT)
|
||||||
|
if (WIN32 OR APPLE OR HAIKU)
|
||||||
|
set(X11_DEFAULT OFF)
|
||||||
|
endif()
|
||||||
|
option(X11 "Whether to enable X11 specific features (screenshare, window roles)." ${X11_DEFAULT})
|
||||||
cmake_dependent_option(SCREENSHARE_XDP "Whether to enable screenshare support using xdg-desktop-portal." ON "VOIP" OFF)
|
cmake_dependent_option(SCREENSHARE_XDP "Whether to enable screenshare support using xdg-desktop-portal." ON "VOIP" OFF)
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||||
|
@ -239,16 +243,17 @@ endif()
|
||||||
#
|
#
|
||||||
# Discover Qt dependencies.
|
# Discover Qt dependencies.
|
||||||
#
|
#
|
||||||
find_package(Qt5 5.15 COMPONENTS Core Widgets LinguistTools Concurrent Svg Multimedia Qml QuickControls2 QuickWidgets REQUIRED)
|
find_package(Qt6 6.5 COMPONENTS Core Widgets Gui LinguistTools Svg Multimedia Qml QuickControls2 REQUIRED)
|
||||||
find_package(Qt5QuickCompiler)
|
#find_package(Qt6QuickCompiler)
|
||||||
find_package(Qt5DBus)
|
find_package(Qt6DBus)
|
||||||
|
|
||||||
if (USE_BUNDLED_QTKEYCHAIN)
|
if (USE_BUNDLED_QTKEYCHAIN)
|
||||||
include(FetchContent)
|
include(FetchContent)
|
||||||
|
set(BUILD_WITH_QT6 ON)
|
||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
qt5keychain
|
qt6keychain
|
||||||
GIT_REPOSITORY https://github.com/frankosterfeld/qtkeychain.git
|
GIT_REPOSITORY https://github.com/frankosterfeld/qtkeychain.git
|
||||||
GIT_TAG v0.13.1
|
GIT_TAG v0.14.0
|
||||||
)
|
)
|
||||||
if (BUILD_SHARED_LIBS)
|
if (BUILD_SHARED_LIBS)
|
||||||
set(QTKEYCHAIN_STATIC OFF CACHE INTERNAL "")
|
set(QTKEYCHAIN_STATIC OFF CACHE INTERNAL "")
|
||||||
|
@ -256,21 +261,17 @@ if (USE_BUNDLED_QTKEYCHAIN)
|
||||||
set(QTKEYCHAIN_STATIC ON CACHE INTERNAL "")
|
set(QTKEYCHAIN_STATIC ON CACHE INTERNAL "")
|
||||||
endif()
|
endif()
|
||||||
set(BUILD_TEST_APPLICATION OFF CACHE INTERNAL "")
|
set(BUILD_TEST_APPLICATION OFF CACHE INTERNAL "")
|
||||||
FetchContent_MakeAvailable(qt5keychain)
|
FetchContent_MakeAvailable(qt6keychain)
|
||||||
else()
|
else()
|
||||||
find_package(Qt5Keychain REQUIRED)
|
find_package(Qt6Keychain REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (APPLE)
|
if (Qt6Widgets_FOUND)
|
||||||
find_package(Qt5MacExtras REQUIRED)
|
if (Qt6Widgets_VERSION VERSION_LESS 6.5.0)
|
||||||
endif(APPLE)
|
message(STATUS "Qt version ${Qt6Widgets_VERSION}")
|
||||||
|
message(WARNING "Minimum supported Qt6 version is 6.5!")
|
||||||
if (Qt5Widgets_FOUND)
|
|
||||||
if (Qt5Widgets_VERSION VERSION_LESS 5.15.0)
|
|
||||||
message(STATUS "Qt version ${Qt5Widgets_VERSION}")
|
|
||||||
message(WARNING "Minimum supported Qt5 version is 5.15!")
|
|
||||||
endif()
|
endif()
|
||||||
endif(Qt5Widgets_FOUND)
|
endif(Qt6Widgets_FOUND)
|
||||||
|
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
if(NOT MSVC)
|
if(NOT MSVC)
|
||||||
|
@ -386,6 +387,8 @@ set(SRC_FILES
|
||||||
# UI components
|
# UI components
|
||||||
src/ui/HiddenEvents.cpp
|
src/ui/HiddenEvents.cpp
|
||||||
src/ui/HiddenEvents.h
|
src/ui/HiddenEvents.h
|
||||||
|
src/ui/EventExpiry.cpp
|
||||||
|
src/ui/EventExpiry.h
|
||||||
src/ui/MxcAnimatedImage.cpp
|
src/ui/MxcAnimatedImage.cpp
|
||||||
src/ui/MxcAnimatedImage.h
|
src/ui/MxcAnimatedImage.h
|
||||||
src/ui/MxcMediaProxy.cpp
|
src/ui/MxcMediaProxy.cpp
|
||||||
|
@ -394,8 +397,6 @@ set(SRC_FILES
|
||||||
src/ui/NhekoCursorShape.h
|
src/ui/NhekoCursorShape.h
|
||||||
src/ui/NhekoDropArea.cpp
|
src/ui/NhekoDropArea.cpp
|
||||||
src/ui/NhekoDropArea.h
|
src/ui/NhekoDropArea.h
|
||||||
src/ui/NhekoEventObserver.cpp
|
|
||||||
src/ui/NhekoEventObserver.h
|
|
||||||
src/ui/NhekoGlobalObject.cpp
|
src/ui/NhekoGlobalObject.cpp
|
||||||
src/ui/NhekoGlobalObject.h
|
src/ui/NhekoGlobalObject.h
|
||||||
src/ui/RoomSettings.cpp
|
src/ui/RoomSettings.cpp
|
||||||
|
@ -498,8 +499,6 @@ set(SRC_FILES
|
||||||
src/SingleImagePackModel.h
|
src/SingleImagePackModel.h
|
||||||
src/TrayIcon.cpp
|
src/TrayIcon.cpp
|
||||||
src/TrayIcon.h
|
src/TrayIcon.h
|
||||||
src/UserDirectoryModel.cpp
|
|
||||||
src/UserDirectoryModel.h
|
|
||||||
src/UserSettingsPage.cpp
|
src/UserSettingsPage.cpp
|
||||||
src/UserSettingsPage.h
|
src/UserSettingsPage.h
|
||||||
src/UsersModel.cpp
|
src/UsersModel.cpp
|
||||||
|
@ -602,7 +601,7 @@ if(USE_BUNDLED_MTXCLIENT)
|
||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
MatrixClient
|
MatrixClient
|
||||||
GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
|
GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
|
||||||
GIT_TAG e136bc27b28d3bb5683735eb5a65d6ef2534ca3a
|
GIT_TAG 0a4cc9421a97bea81a8921f3f5e040f0a34278fc
|
||||||
)
|
)
|
||||||
set(BUILD_LIB_EXAMPLES OFF CACHE INTERNAL "")
|
set(BUILD_LIB_EXAMPLES OFF CACHE INTERNAL "")
|
||||||
set(BUILD_LIB_TESTS OFF CACHE INTERNAL "")
|
set(BUILD_LIB_TESTS OFF CACHE INTERNAL "")
|
||||||
|
@ -614,9 +613,10 @@ endif()
|
||||||
if (VOIP)
|
if (VOIP)
|
||||||
include(FindPkgConfig)
|
include(FindPkgConfig)
|
||||||
pkg_check_modules(GSTREAMER REQUIRED IMPORTED_TARGET gstreamer-sdp-1.0>=1.18 gstreamer-webrtc-1.0>=1.18)
|
pkg_check_modules(GSTREAMER REQUIRED IMPORTED_TARGET gstreamer-sdp-1.0>=1.18 gstreamer-webrtc-1.0>=1.18)
|
||||||
if (SCREENSHARE_X11 AND NOT WIN32 AND NOT APPLE)
|
endif()
|
||||||
pkg_check_modules(XCB REQUIRED IMPORTED_TARGET xcb xcb-ewmh)
|
|
||||||
endif()
|
if (X11 AND NOT WIN32 AND NOT APPLE AND NOT HAIKU)
|
||||||
|
pkg_check_modules(XCB REQUIRED IMPORTED_TARGET xcb xcb-ewmh)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# single instance functionality
|
# single instance functionality
|
||||||
|
@ -631,10 +631,13 @@ if (NOT APPLE AND NOT WIN32)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
#
|
#
|
||||||
# Bundle translations.
|
# Bundle resources
|
||||||
#
|
#
|
||||||
include(Translations)
|
if(Qt6QuickCompiler_FOUND AND COMPILE_QML)
|
||||||
set(TRANSLATION_DEPS ${LANG_QRC} ${QRC} ${QM_SRC})
|
qtquick_compiler_add_resources(QRC resources/res.qrc)
|
||||||
|
else()
|
||||||
|
qt_add_resources(QRC resources/res.qrc)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Foundation -framework Cocoa -framework UserNotifications")
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Foundation -framework Cocoa -framework UserNotifications")
|
||||||
|
@ -666,7 +669,7 @@ endif ()
|
||||||
|
|
||||||
set(NHEKO_DEPS
|
set(NHEKO_DEPS
|
||||||
${SRC_FILES}
|
${SRC_FILES}
|
||||||
${TRANSLATION_DEPS}
|
${QRC}
|
||||||
${META_FILES_TO_INCLUDE})
|
${META_FILES_TO_INCLUDE})
|
||||||
|
|
||||||
if(ASAN)
|
if(ASAN)
|
||||||
|
@ -674,10 +677,10 @@ if(ASAN)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
add_executable (nheko WIN32 ${OS_BUNDLE} ${NHEKO_DEPS})
|
qt_add_executable (nheko WIN32 ${OS_BUNDLE} ${NHEKO_DEPS})
|
||||||
target_compile_definitions(nheko PRIVATE _WIN32_WINNT=0x0601 NOMINMAX WIN32_LEAN_AND_MEAN STRICT)
|
target_compile_definitions(nheko PRIVATE _WIN32_WINNT=0x0601 NOMINMAX WIN32_LEAN_AND_MEAN STRICT)
|
||||||
else()
|
else()
|
||||||
add_executable (nheko ${OS_BUNDLE} ${NHEKO_DEPS})
|
qt_add_executable (nheko ${OS_BUNDLE} ${NHEKO_DEPS})
|
||||||
|
|
||||||
if (HAVE_BACKTRACE_SYMBOLS_FD AND NOT CMAKE_BUILD_TYPE STREQUAL "Release")
|
if (HAVE_BACKTRACE_SYMBOLS_FD AND NOT CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
set_target_properties(nheko PROPERTIES ENABLE_EXPORTS ON)
|
set_target_properties(nheko PROPERTIES ENABLE_EXPORTS ON)
|
||||||
|
@ -689,22 +692,150 @@ set_target_properties(nheko
|
||||||
CMAKE_SKIP_INSTALL_RPATH TRUE
|
CMAKE_SKIP_INSTALL_RPATH TRUE
|
||||||
AUTOMOC ON)
|
AUTOMOC ON)
|
||||||
|
|
||||||
if(APPLE)
|
#
|
||||||
target_link_libraries (nheko PRIVATE Qt5::MacExtras)
|
# Bundle translations
|
||||||
elseif(WIN32)
|
#
|
||||||
|
file(GLOB LANG_TS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/resources/langs/*.ts")
|
||||||
|
qt_add_translations(nheko RESOURCE_PREFIX "/translations" TS_FILES ${LANG_TS_SRC})
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Add qml files
|
||||||
|
#
|
||||||
|
|
||||||
|
set(QML_SOURCES
|
||||||
|
resources/qml/Root.qml
|
||||||
|
resources/qml/ChatPage.qml
|
||||||
|
resources/qml/CommunitiesList.qml
|
||||||
|
resources/qml/RoomList.qml
|
||||||
|
resources/qml/TimelineView.qml
|
||||||
|
resources/qml/Avatar.qml
|
||||||
|
resources/qml/Completer.qml
|
||||||
|
resources/qml/EncryptionIndicator.qml
|
||||||
|
resources/qml/ImageButton.qml
|
||||||
|
resources/qml/ElidedLabel.qml
|
||||||
|
resources/qml/MatrixText.qml
|
||||||
|
resources/qml/MatrixTextField.qml
|
||||||
|
resources/qml/ToggleButton.qml
|
||||||
|
resources/qml/UploadBox.qml
|
||||||
|
resources/qml/MessageInput.qml
|
||||||
|
resources/qml/MessageView.qml
|
||||||
|
resources/qml/PrivacyScreen.qml
|
||||||
|
resources/qml/Reactions.qml
|
||||||
|
resources/qml/ReplyPopup.qml
|
||||||
|
resources/qml/StatusIndicator.qml
|
||||||
|
resources/qml/TimelineRow.qml
|
||||||
|
resources/qml/TopBar.qml
|
||||||
|
resources/qml/QuickSwitcher.qml
|
||||||
|
resources/qml/ForwardCompleter.qml
|
||||||
|
resources/qml/SelfVerificationCheck.qml
|
||||||
|
resources/qml/TypingIndicator.qml
|
||||||
|
resources/qml/MessageInputWarning.qml
|
||||||
|
resources/qml/components/AdaptiveLayout.qml
|
||||||
|
resources/qml/components/AdaptiveLayoutElement.qml
|
||||||
|
resources/qml/components/AvatarListTile.qml
|
||||||
|
resources/qml/components/FlatButton.qml
|
||||||
|
resources/qml/components/MainWindowDialog.qml
|
||||||
|
resources/qml/components/NhekoTabButton.qml
|
||||||
|
resources/qml/components/NotificationBubble.qml
|
||||||
|
resources/qml/components/ReorderableListview.qml
|
||||||
|
resources/qml/components/SpaceMenuLevel.qml
|
||||||
|
resources/qml/components/TextButton.qml
|
||||||
|
resources/qml/components/UserListRow.qml
|
||||||
|
resources/qml/delegates/Encrypted.qml
|
||||||
|
resources/qml/delegates/FileMessage.qml
|
||||||
|
resources/qml/delegates/ImageMessage.qml
|
||||||
|
resources/qml/delegates/MessageDelegate.qml
|
||||||
|
resources/qml/delegates/NoticeMessage.qml
|
||||||
|
resources/qml/delegates/Pill.qml
|
||||||
|
resources/qml/delegates/Placeholder.qml
|
||||||
|
resources/qml/delegates/PlayableMediaMessage.qml
|
||||||
|
resources/qml/delegates/Redacted.qml
|
||||||
|
resources/qml/delegates/Reply.qml
|
||||||
|
resources/qml/delegates/TextMessage.qml
|
||||||
|
resources/qml/device-verification/DeviceVerification.qml
|
||||||
|
resources/qml/device-verification/DigitVerification.qml
|
||||||
|
resources/qml/device-verification/EmojiVerification.qml
|
||||||
|
resources/qml/device-verification/Failed.qml
|
||||||
|
resources/qml/device-verification/NewVerificationRequest.qml
|
||||||
|
resources/qml/device-verification/Success.qml
|
||||||
|
resources/qml/device-verification/Waiting.qml
|
||||||
|
resources/qml/dialogs/AliasEditor.qml
|
||||||
|
resources/qml/dialogs/ConfirmJoinRoomDialog.qml
|
||||||
|
resources/qml/dialogs/CreateDirect.qml
|
||||||
|
resources/qml/dialogs/CreateRoom.qml
|
||||||
|
resources/qml/dialogs/HiddenEventsDialog.qml
|
||||||
|
resources/qml/dialogs/EventExpirationDialog.qml
|
||||||
|
resources/qml/dialogs/ImageOverlay.qml
|
||||||
|
resources/qml/dialogs/ImagePackEditorDialog.qml
|
||||||
|
resources/qml/dialogs/ImagePackSettingsDialog.qml
|
||||||
|
resources/qml/dialogs/InputDialog.qml
|
||||||
|
resources/qml/dialogs/InviteDialog.qml
|
||||||
|
resources/qml/dialogs/JoinRoomDialog.qml
|
||||||
|
resources/qml/dialogs/LeaveRoomDialog.qml
|
||||||
|
resources/qml/dialogs/LogoutDialog.qml
|
||||||
|
resources/qml/dialogs/PhoneNumberInputDialog.qml
|
||||||
|
resources/qml/dialogs/PowerLevelEditor.qml
|
||||||
|
resources/qml/dialogs/PowerLevelSpacesApplyDialog.qml
|
||||||
|
resources/qml/dialogs/RawMessageDialog.qml
|
||||||
|
resources/qml/dialogs/ReadReceipts.qml
|
||||||
|
resources/qml/dialogs/RoomDirectory.qml
|
||||||
|
resources/qml/dialogs/RoomMembers.qml
|
||||||
|
resources/qml/dialogs/AllowedRoomsSettingsDialog.qml
|
||||||
|
resources/qml/dialogs/RoomSettings.qml
|
||||||
|
resources/qml/dialogs/UserProfile.qml
|
||||||
|
resources/qml/emoji/StickerPicker.qml
|
||||||
|
resources/qml/pages/LoginPage.qml
|
||||||
|
resources/qml/pages/RegisterPage.qml
|
||||||
|
resources/qml/pages/UserSettingsPage.qml
|
||||||
|
resources/qml/pages/WelcomePage.qml
|
||||||
|
resources/qml/ui/NhekoSlider.qml
|
||||||
|
resources/qml/ui/Ripple.qml
|
||||||
|
resources/qml/ui/Snackbar.qml
|
||||||
|
resources/qml/ui/Spinner.qml
|
||||||
|
resources/qml/ui/animations/BlinkAnimation.qml
|
||||||
|
resources/qml/ui/media/MediaControls.qml
|
||||||
|
resources/qml/voip/ActiveCallBar.qml
|
||||||
|
resources/qml/voip/CallDevices.qml
|
||||||
|
resources/qml/voip/CallInvite.qml
|
||||||
|
resources/qml/voip/CallInviteBar.qml
|
||||||
|
resources/qml/voip/DeviceError.qml
|
||||||
|
resources/qml/voip/PlaceCall.qml
|
||||||
|
resources/qml/voip/ScreenShare.qml
|
||||||
|
resources/qml/voip/VideoCall.qml
|
||||||
|
resources/qml/delegates/EncryptionEnabled.qml
|
||||||
|
resources/qml/ui/TimelineEffects.qml
|
||||||
|
)
|
||||||
|
qt_add_qml_module(nheko
|
||||||
|
URI im.nheko
|
||||||
|
NO_RESOURCE_TARGET_PATH
|
||||||
|
RESOURCE_PREFIX "/"
|
||||||
|
VERSION 1.1
|
||||||
|
DEPENDENCIES QtQml QtQuick # https://bugreports.qt.io/browse/QTBUG-102554
|
||||||
|
QML_FILES
|
||||||
|
${QML_SOURCES}
|
||||||
|
SOURCES
|
||||||
|
src/UserDirectoryModel.cpp
|
||||||
|
src/UserDirectoryModel.h
|
||||||
|
)
|
||||||
|
#qt_target_qml_sources(nheko
|
||||||
|
# #PREFIX "/"
|
||||||
|
#)
|
||||||
|
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
target_compile_definitions(nheko PRIVATE WIN32_LEAN_AND_MEAN)
|
target_compile_definitions(nheko PRIVATE WIN32_LEAN_AND_MEAN)
|
||||||
target_link_libraries (nheko PRIVATE ${NTDLIB} Qt5::WinMain)
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
target_compile_options(nheko PUBLIC "/Zc:__cplusplus")
|
target_compile_options(nheko PUBLIC "/Zc:__cplusplus")
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
target_link_libraries (nheko PRIVATE Qt5::DBus)
|
target_link_libraries (nheko PRIVATE Qt6::DBus)
|
||||||
if (FLATPAK)
|
if (FLATPAK)
|
||||||
target_compile_definitions(nheko PRIVATE NHEKO_FLATPAK)
|
target_compile_definitions(nheko PRIVATE NHEKO_FLATPAK)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(nheko PRIVATE src includes)
|
target_include_directories(nheko PRIVATE src includes src/timeline/ src/ui/ src/encryption/ src/voip/)
|
||||||
|
|
||||||
if (USE_BUNDLED_CPPHTTPLIB)
|
if (USE_BUNDLED_CPPHTTPLIB)
|
||||||
target_include_directories(nheko PRIVATE third_party/cpp-httplib-0.5.12)
|
target_include_directories(nheko PRIVATE third_party/cpp-httplib-0.5.12)
|
||||||
|
@ -729,7 +860,7 @@ endif()
|
||||||
|
|
||||||
# Fixup bundled keychain include dirs
|
# Fixup bundled keychain include dirs
|
||||||
if (USE_BUNDLED_QTKEYCHAIN)
|
if (USE_BUNDLED_QTKEYCHAIN)
|
||||||
target_include_directories(nheko PRIVATE ${qt5keychain_SOURCE_DIR} ${qt5keychain_BINARY_DIR})
|
target_include_directories(nheko PRIVATE ${qt6keychain_SOURCE_DIR} ${qt6keychain_BINARY_DIR})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (NOT JSON_ImplicitConversions)
|
if (NOT JSON_ImplicitConversions)
|
||||||
|
@ -744,14 +875,13 @@ target_link_libraries(nheko PRIVATE
|
||||||
MatrixClient::MatrixClient
|
MatrixClient::MatrixClient
|
||||||
cmark::cmark
|
cmark::cmark
|
||||||
spdlog::spdlog
|
spdlog::spdlog
|
||||||
Qt5::Widgets
|
Qt::Widgets
|
||||||
Qt5::Svg
|
Qt::Svg
|
||||||
Qt5::Concurrent
|
Qt::Gui
|
||||||
Qt5::Multimedia
|
Qt::Multimedia
|
||||||
Qt5::Qml
|
Qt::Qml
|
||||||
Qt5::QuickControls2
|
Qt::QuickControls2
|
||||||
Qt5::QuickWidgets
|
qt6keychain
|
||||||
qt5keychain
|
|
||||||
nlohmann_json::nlohmann_json
|
nlohmann_json::nlohmann_json
|
||||||
lmdbxx::lmdbxx
|
lmdbxx::lmdbxx
|
||||||
liblmdb::lmdb
|
liblmdb::lmdb
|
||||||
|
@ -768,10 +898,10 @@ endif()
|
||||||
if (TARGET PkgConfig::GSTREAMER)
|
if (TARGET PkgConfig::GSTREAMER)
|
||||||
target_link_libraries(nheko PRIVATE PkgConfig::GSTREAMER)
|
target_link_libraries(nheko PRIVATE PkgConfig::GSTREAMER)
|
||||||
target_compile_definitions(nheko PRIVATE GSTREAMER_AVAILABLE)
|
target_compile_definitions(nheko PRIVATE GSTREAMER_AVAILABLE)
|
||||||
if (TARGET PkgConfig::XCB)
|
endif()
|
||||||
target_link_libraries(nheko PRIVATE PkgConfig::XCB)
|
if (TARGET PkgConfig::XCB)
|
||||||
target_compile_definitions(nheko PRIVATE XCB_AVAILABLE)
|
target_link_libraries(nheko PRIVATE PkgConfig::XCB)
|
||||||
endif()
|
target_compile_definitions(nheko PRIVATE XCB_AVAILABLE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
|
@ -797,9 +927,23 @@ if(MAN)
|
||||||
add_subdirectory(man)
|
add_subdirectory(man)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# potential workaround for macdeployqt issues
|
||||||
|
if(APPLE)
|
||||||
|
install(TARGETS nheko
|
||||||
|
BUNDLE DESTINATION .
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
|
)
|
||||||
|
qt_generate_deploy_qml_app_script(
|
||||||
|
TARGET nheko
|
||||||
|
OUTPUT_SCRIPT deploy_script
|
||||||
|
NO_UNSUPPORTED_PLATFORM_ERROR
|
||||||
|
)
|
||||||
|
install(SCRIPT ${deploy_script})
|
||||||
|
endif()
|
||||||
|
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
if(FLATPAK)
|
if(FLATPAK)
|
||||||
set(APPID "io.github.NhekoReborn.Nheko")
|
set(APPID "im.nheko.Nheko")
|
||||||
set_target_properties(nheko PROPERTIES OUTPUT_NAME "${APPID}")
|
set_target_properties(nheko PROPERTIES OUTPUT_NAME "${APPID}")
|
||||||
else()
|
else()
|
||||||
set(APPID "nheko")
|
set(APPID "nheko")
|
||||||
|
|
|
@ -4,7 +4,7 @@ nheko
|
||||||
[![Build status](https://ci.appveyor.com/api/projects/status/07qrqbfylsg4hw2h/branch/master?svg=true)](https://ci.appveyor.com/project/redsky17/nheko/branch/master)
|
[![Build status](https://ci.appveyor.com/api/projects/status/07qrqbfylsg4hw2h/branch/master?svg=true)](https://ci.appveyor.com/project/redsky17/nheko/branch/master)
|
||||||
[![Stable Version](https://img.shields.io/badge/download-stable-green.svg)](https://github.com/Nheko-Reborn/nheko/releases/latest)
|
[![Stable Version](https://img.shields.io/badge/download-stable-green.svg)](https://github.com/Nheko-Reborn/nheko/releases/latest)
|
||||||
[![Nightly](https://img.shields.io/badge/download-nightly-green.svg)](https://matrix-static.neko.dev/room/!TshDrgpBNBDmfDeEGN:neko.dev/)
|
[![Nightly](https://img.shields.io/badge/download-nightly-green.svg)](https://matrix-static.neko.dev/room/!TshDrgpBNBDmfDeEGN:neko.dev/)
|
||||||
<a href='https://flatpak.neko.dev/repo/nightly/appstream/io.github.NhekoReborn.Nheko.flatpakref' download='nheko-nightly.flatpakref'><img alt='Download Nightly Flatpak' src='https://img.shields.io/badge/download-flatpak--nightly-green'/></a>
|
<a href='https://flatpak.neko.dev/repo/nightly/appstream/im.nheko.Nheko.flatpakref' download='nheko-nightly.flatpakref'><img alt='Download Nightly Flatpak' src='https://img.shields.io/badge/download-flatpak--nightly-green'/></a>
|
||||||
[![#nheko-reborn:matrix.org](https://img.shields.io/matrix/nheko-reborn:matrix.org.svg?label=%23nheko-reborn:matrix.org)](https://matrix.to/#/#nheko-reborn:matrix.org)
|
[![#nheko-reborn:matrix.org](https://img.shields.io/matrix/nheko-reborn:matrix.org.svg?label=%23nheko-reborn:matrix.org)](https://matrix.to/#/#nheko-reborn:matrix.org)
|
||||||
[![Arch package](https://repology.org/badge/version-for-repo/arch/nheko.svg)](https://archlinux.org/packages/community/x86_64/nheko/)
|
[![Arch package](https://repology.org/badge/version-for-repo/arch/nheko.svg)](https://archlinux.org/packages/community/x86_64/nheko/)
|
||||||
<a href='https://flathub.org/apps/details/io.github.NhekoReborn.Nheko'><img width='240' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-en.png'/></a>
|
<a href='https://flathub.org/apps/details/io.github.NhekoReborn.Nheko'><img width='240' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-en.png'/></a>
|
||||||
|
@ -249,7 +249,7 @@ KDE has similar plugins, that can extend the supported image types even more.
|
||||||
- Voice call support: dtls, opus, rtpmanager, srtp, webrtc
|
- Voice call support: dtls, opus, rtpmanager, srtp, webrtc
|
||||||
- Video call support (optional): compositor, opengl, qmlgl, rtp, vpx
|
- Video call support (optional): compositor, opengl, qmlgl, rtp, vpx
|
||||||
- [libnice](https://gitlab.freedesktop.org/libnice/libnice)
|
- [libnice](https://gitlab.freedesktop.org/libnice/libnice)
|
||||||
- XCB, XCB-EWMH: For screensharing support on X11. VOIP needs to be enabled. Can be disabled with `-DSCREENSHARE_X11=OFF`.
|
- XCB, XCB-EWMH: For screensharing support on X11 and setting window roles. Can be disabled with `-DSCREENSHARE_X11=OFF`.
|
||||||
- [qtkeychain](https://github.com/frankosterfeld/qtkeychain) (You need at least version 0.12 for proper Gnome Keychain support. The bundled version requires libsecret, unless you pass `-DLIBSECRET_SUPPORT=OFF`.)
|
- [qtkeychain](https://github.com/frankosterfeld/qtkeychain) (You need at least version 0.12 for proper Gnome Keychain support. The bundled version requires libsecret, unless you pass `-DLIBSECRET_SUPPORT=OFF`.)
|
||||||
- A compiler that supports C++ 20:
|
- A compiler that supports C++ 20:
|
||||||
- Clang 16 (Only clazy 16 is tested in CI)
|
- Clang 16 (Only clazy 16 is tested in CI)
|
||||||
|
|
|
@ -22,7 +22,7 @@ build:
|
||||||
verbosity: minimal
|
verbosity: minimal
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- set QT_DIR=C:\Qt\5.15\msvc2019_64
|
- set QT_DIR=C:\Qt\6.5\msvc2019_64
|
||||||
- set PATH=C:\Strawberry\perl\bin;C:\Python39-x64;%QT_DIR%\bin;%PATH%
|
- set PATH=C:\Strawberry\perl\bin;C:\Python39-x64;%QT_DIR%\bin;%PATH%
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
#
|
|
||||||
# Generate the translation resource file
|
|
||||||
#
|
|
||||||
|
|
||||||
file(GLOB LANG_TS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/resources/langs/*.ts")
|
|
||||||
|
|
||||||
qt5_add_translation(QM_SRC ${LANG_TS_SRC})
|
|
||||||
qt5_create_translation(${QM_SRC})
|
|
||||||
add_custom_target(LANG_QRC ALL DEPENDS ${QM_SRC})
|
|
||||||
|
|
||||||
# Generate a qrc file for the translations
|
|
||||||
set(_qrc ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc)
|
|
||||||
|
|
||||||
if(NOT EXISTS ${_qrc})
|
|
||||||
file(WRITE ${_qrc} "<RCC>\n <qresource prefix=\"/translations\">\n")
|
|
||||||
foreach(_lang ${QM_SRC})
|
|
||||||
get_filename_component(_filename ${_lang} NAME)
|
|
||||||
file(APPEND ${_qrc} " <file>${_filename}</file>\n")
|
|
||||||
endforeach(_lang)
|
|
||||||
file(APPEND ${_qrc} " </qresource>\n</RCC>\n")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
qt5_add_resources(LANG_QRC ${_qrc})
|
|
||||||
if(Qt5QuickCompiler_FOUND AND COMPILE_QML)
|
|
||||||
qtquick_compiler_add_resources(QRC resources/res.qrc)
|
|
||||||
else()
|
|
||||||
qt5_add_resources(QRC resources/res.qrc)
|
|
||||||
endif()
|
|
|
@ -1,7 +1,7 @@
|
||||||
id: io.github.NhekoReborn.Nheko
|
id: im.nheko.Nheko
|
||||||
command: io.github.NhekoReborn.Nheko
|
command: im.nheko.Nheko
|
||||||
runtime: org.kde.Platform
|
runtime: org.kde.Platform
|
||||||
runtime-version: '5.15-22.08'
|
runtime-version: '6.5'
|
||||||
sdk: org.kde.Sdk
|
sdk: org.kde.Sdk
|
||||||
finish-args:
|
finish-args:
|
||||||
- --device=dri
|
- --device=dri
|
||||||
|
@ -44,9 +44,9 @@ cleanup:
|
||||||
modules:
|
modules:
|
||||||
- name: lmdb
|
- name: lmdb
|
||||||
sources:
|
sources:
|
||||||
- sha256: f3927859882eb608868c8c31586bb7eb84562a40a6bf5cc3e13b6b564641ea28
|
- sha256: 8c5a93ac3cc97427c54571ad5a6140b7469389d01e6d2f43df39f96d3a4ccef7
|
||||||
type: archive
|
type: archive
|
||||||
url: https://github.com/LMDB/lmdb/archive/LMDB_0.9.22.tar.gz
|
url: https://git.openldap.org/openldap/openldap/-/archive/LMDB_0.9.30/openldap-LMDB_0.9.30.tar.gz
|
||||||
make-install-args:
|
make-install-args:
|
||||||
- prefix=/app
|
- prefix=/app
|
||||||
no-autogen: true
|
no-autogen: true
|
||||||
|
@ -109,45 +109,46 @@ modules:
|
||||||
tag: 0.20.4
|
tag: 0.20.4
|
||||||
type: git
|
type: git
|
||||||
url: https://gitlab.gnome.org/GNOME/libsecret.git
|
url: https://gitlab.gnome.org/GNOME/libsecret.git
|
||||||
- config-opts:
|
#- config-opts:
|
||||||
- -DCMAKE_BUILD_TYPE=Release
|
# - -DCMAKE_BUILD_TYPE=Release
|
||||||
- -DAVIF_CODEC_AOM=ON
|
# - -DAVIF_CODEC_AOM=ON
|
||||||
#- -DBUILD_SHARED_LIBS=OFF
|
# #- -DBUILD_SHARED_LIBS=OFF
|
||||||
buildsystem: cmake-ninja
|
# buildsystem: cmake-ninja
|
||||||
name: libavif
|
# name: libavif
|
||||||
sources:
|
# sources:
|
||||||
- sha256: 66e82854ceb84a3e542bc140a343bc90e56c68f3ecb4fff63e636c136ed9a05e
|
# - sha256: 66e82854ceb84a3e542bc140a343bc90e56c68f3ecb4fff63e636c136ed9a05e
|
||||||
type: archive
|
# type: archive
|
||||||
url: https://github.com/AOMediaCodec/libavif/archive/refs/tags/v0.10.1.tar.gz
|
# url: https://github.com/AOMediaCodec/libavif/archive/refs/tags/v0.10.1.tar.gz
|
||||||
- config-opts:
|
#- config-opts:
|
||||||
- -DCMAKE_BUILD_TYPE=Release
|
# - -DCMAKE_BUILD_TYPE=Release
|
||||||
- -DWITH_EXAMPLES=OFF
|
# - -DWITH_EXAMPLES=OFF
|
||||||
#- -DBUILD_SHARED_LIBS=OFF
|
# #- -DBUILD_SHARED_LIBS=OFF
|
||||||
buildsystem: cmake-ninja
|
# buildsystem: cmake-ninja
|
||||||
name: libheif
|
# name: libheif
|
||||||
sources:
|
# sources:
|
||||||
- sha256: e1ac2abb354fdc8ccdca71363ebad7503ad731c84022cf460837f0839e171718
|
# - sha256: e1ac2abb354fdc8ccdca71363ebad7503ad731c84022cf460837f0839e171718
|
||||||
type: archive
|
# type: archive
|
||||||
url: https://github.com/strukturag/libheif/releases/download/v1.12.0/libheif-1.12.0.tar.gz
|
# url: https://github.com/strukturag/libheif/releases/download/v1.12.0/libheif-1.12.0.tar.gz
|
||||||
- config-opts:
|
#- config-opts:
|
||||||
- -DCMAKE_BUILD_TYPE=Release
|
# - -DCMAKE_BUILD_TYPE=Release
|
||||||
- -DKIMAGEFORMATS_HEIF=ON
|
# - -DKIMAGEFORMATS_HEIF=ON
|
||||||
buildsystem: cmake-ninja
|
# buildsystem: cmake-ninja
|
||||||
name: KImageFormats
|
# name: KImageFormats
|
||||||
sources:
|
# sources:
|
||||||
- commit: ae6b724824fc2fdf71d50dc7ae0052ad1551b25a
|
# - commit: ae6b724824fc2fdf71d50dc7ae0052ad1551b25a
|
||||||
tag: v5.93.0
|
# tag: v5.93.0
|
||||||
type: git
|
# type: git
|
||||||
url: https://invent.kde.org/frameworks/kimageformats.git
|
# url: https://invent.kde.org/frameworks/kimageformats.git
|
||||||
- config-opts:
|
- config-opts:
|
||||||
- -DCMAKE_BUILD_TYPE=Release
|
- -DCMAKE_BUILD_TYPE=Release
|
||||||
- -DBUILD_TEST_APPLICATION=OFF
|
- -DBUILD_TEST_APPLICATION=OFF
|
||||||
- -DQTKEYCHAIN_STATIC=ON
|
- -DQTKEYCHAIN_STATIC=ON
|
||||||
|
- -DBUILD_WITH_QT6=ON
|
||||||
buildsystem: cmake-ninja
|
buildsystem: cmake-ninja
|
||||||
name: QtKeychain
|
name: QtKeychain
|
||||||
sources:
|
sources:
|
||||||
- commit: f59ac26be709fd2d8d7a062fab1cf1e67a93806c
|
- commit: 69f993c47efed7e557d79a30a367014d9a27d809
|
||||||
tag: v0.13.1
|
tag: 0.14.1
|
||||||
type: git
|
type: git
|
||||||
url: https://github.com/frankosterfeld/qtkeychain.git
|
url: https://github.com/frankosterfeld/qtkeychain.git
|
||||||
- config-opts:
|
- config-opts:
|
||||||
|
@ -170,15 +171,15 @@ modules:
|
||||||
- buildsystem: meson
|
- buildsystem: meson
|
||||||
name: gstreamer
|
name: gstreamer
|
||||||
sources:
|
sources:
|
||||||
- commit: f7806a854aad960eae3288db4a67a574f92428fe
|
- commit: ecd471f5ea4645102b206a43d863f0f0fe7d04ec
|
||||||
tag: 1.20.5
|
tag: 1.22.3
|
||||||
type: git
|
type: git
|
||||||
url: https://gitlab.freedesktop.org/gstreamer/gstreamer.git
|
url: https://gitlab.freedesktop.org/gstreamer/gstreamer.git
|
||||||
config-opts:
|
config-opts:
|
||||||
- --auto-features=disabled
|
- --auto-features=disabled
|
||||||
- -Dgood=enabled
|
- -Dgood=enabled
|
||||||
- -Dgst-plugins-good:qt5=enabled
|
- -Dgst-plugins-good:qt6=enabled
|
||||||
- -Dqt5=enabled
|
#- -Dqt6=enabled <- not available on 1.22
|
||||||
- -Dbase=enabled
|
- -Dbase=enabled
|
||||||
- -Dgst-plugins-base:gl=enabled
|
- -Dgst-plugins-base:gl=enabled
|
||||||
- -Dgst-plugins-base:gl_platform=glx,egl
|
- -Dgst-plugins-base:gl_platform=glx,egl
|
||||||
|
@ -213,7 +214,7 @@ modules:
|
||||||
buildsystem: cmake-ninja
|
buildsystem: cmake-ninja
|
||||||
name: mtxclient
|
name: mtxclient
|
||||||
sources:
|
sources:
|
||||||
- commit: e136bc27b28d3bb5683735eb5a65d6ef2534ca3a
|
- commit: 0a4cc9421a97bea81a8921f3f5e040f0a34278fc
|
||||||
#tag: v0.9.2
|
#tag: v0.9.2
|
||||||
type: git
|
type: git
|
||||||
url: https://github.com/Nheko-Reborn/mtxclient.git
|
url: https://github.com/Nheko-Reborn/mtxclient.git
|
|
@ -1,6 +1,6 @@
|
||||||
[Flatpak Ref]
|
[Flatpak Ref]
|
||||||
Title=Nheko Nightly
|
Title=Nheko Nightly
|
||||||
Name=io.github.NhekoReborn.Nheko
|
Name=im.nheko.Nheko
|
||||||
Branch=master
|
Branch=master
|
||||||
Url=https://flatpak.neko.dev/repo/nightly
|
Url=https://flatpak.neko.dev/repo/nightly
|
||||||
SuggestRemoteName=nheko-nightlies
|
SuggestRemoteName=nheko-nightlies
|
||||||
|
|
|
@ -1390,11 +1390,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1392,11 +1392,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1390,11 +1390,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation type="unfinished">Διάλεξε ένα αρχείο</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1394,11 +1394,6 @@ Vi povas aldoni noton, pri kial oni akceptu vian frapadon:</translation>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation>Elektu dosieron</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1392,11 +1392,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation>Seleccionar un archivo</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1393,11 +1393,6 @@ Kui soovid, siis võid lisada ka selgituse, miks peaks sinu koputusele reageerim
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation>Vali fail</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
@ -1408,6 +1403,11 @@ Kui soovid, siis võid lisada ka selgituse, miks peaks sinu koputusele reageerim
|
||||||
<source>Upload of '%1' failed</source>
|
<source>Upload of '%1' failed</source>
|
||||||
<translation>„%1“ üleslaadimine ei õnnestunud</translation>
|
<translation>„%1“ üleslaadimine ei õnnestunud</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
||||||
|
<source>Select file(s)</source>
|
||||||
|
<translation>Vali fail(id)</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InviteDialog</name>
|
<name>InviteDialog</name>
|
||||||
|
|
|
@ -1393,11 +1393,6 @@ Voit antaa valinnaisen syyn muiden hyväksyäkseen koputuksesi:</translation>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation>Valitse tiedosto</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -96,7 +96,7 @@
|
||||||
<message>
|
<message>
|
||||||
<location line="+12"/>
|
<location line="+12"/>
|
||||||
<source>Add</source>
|
<source>Add</source>
|
||||||
<translation type="unfinished">Ajouter</translation>
|
<translation>Ajouter</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -229,7 +229,7 @@
|
||||||
<message>
|
<message>
|
||||||
<location line="-554"/>
|
<location line="-554"/>
|
||||||
<source>Confirm logout</source>
|
<source>Confirm logout</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Confirmer la déconnexion</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+102"/>
|
<location line="+102"/>
|
||||||
|
@ -239,7 +239,7 @@
|
||||||
<message>
|
<message>
|
||||||
<location line="+35"/>
|
<location line="+35"/>
|
||||||
<source>Failed to open database, logging out!</source>
|
<source>Failed to open database, logging out!</source>
|
||||||
<translation>Impossible d'ouvrir la base de données, déconnexion !</translation>
|
<translation>Impossible d'ouvrir la base de données, déconnexion !</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+262"/>
|
<location line="+262"/>
|
||||||
|
@ -249,7 +249,7 @@
|
||||||
<message>
|
<message>
|
||||||
<location line="+4"/>
|
<location line="+4"/>
|
||||||
<source>Do you really want to knock on %1? You may optionally provide a reason for others to accept your knock:</source>
|
<source>Do you really want to knock on %1? You may optionally provide a reason for others to accept your knock:</source>
|
||||||
<translation>Voulez-vous vraiment toquer à %1 ? Vous pouvez fournir une raison aux autres de l'accepter :</translation>
|
<translation>Voulez-vous vraiment frapper à %1 ? Vous pouvez donner une raison aux membres actuels de vous accepter :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+15"/>
|
<location line="+15"/>
|
||||||
|
@ -275,7 +275,7 @@
|
||||||
<message>
|
<message>
|
||||||
<location line="-470"/>
|
<location line="-470"/>
|
||||||
<source>Do you really want to invite %1 (%2)?</source>
|
<source>Do you really want to invite %1 (%2)?</source>
|
||||||
<translation>Voulez-vous vraiment inviter %1 (%2) ?</translation>
|
<translation>Voulez-vous vraiment inviter %1 (%2) ?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+12"/>
|
<location line="+12"/>
|
||||||
|
@ -305,7 +305,7 @@
|
||||||
<message>
|
<message>
|
||||||
<location line="+1"/>
|
<location line="+1"/>
|
||||||
<source>Do you really want to unban %1 (%2)?</source>
|
<source>Do you really want to unban %1 (%2)?</source>
|
||||||
<translation>Voulez-vous vraiment annuler le bannissement de %1 (%2) ?</translation>
|
<translation>Voulez-vous vraiment annuler le bannissement de %1 (%2) ?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+10"/>
|
<location line="+10"/>
|
||||||
|
@ -325,19 +325,21 @@
|
||||||
<message>
|
<message>
|
||||||
<location line="-922"/>
|
<location line="-922"/>
|
||||||
<source>Cache migration failed!</source>
|
<source>Cache migration failed!</source>
|
||||||
<translation>Échec de la migration du cache !</translation>
|
<translation>Échec de la migration du cache !</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="-87"/>
|
<location line="-87"/>
|
||||||
<source>Because of the following reason Nheko wants to drop you to the login page:
|
<source>Because of the following reason Nheko wants to drop you to the login page:
|
||||||
%1
|
%1
|
||||||
If you think this is a mistake, you can close Nheko instead to possibly recover your encryption keys. After you have been dropped to the login page, you can sign in again using your usual methods.</source>
|
If you think this is a mistake, you can close Nheko instead to possibly recover your encryption keys. After you have been dropped to the login page, you can sign in again using your usual methods.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Nheko veut vous renvoyer à la page de connexion pour cette raison :
|
||||||
|
%1
|
||||||
|
Si vous pensez qu'il s'agit d'une erreur, vous pouvez plutôt fermer Nheko pour essayer de récupérer vos clés de chiffrement. De retour à la page de connexion, vous pourrez vous reconnecter par vos méthodes habituelles.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+88"/>
|
<location line="+88"/>
|
||||||
<source>Migrating the cache to the current version failed. This can have different reasons. Please open an issue at https://github.com/Nheko-Reborn/nheko and try to use an older version in the meantime. Alternatively you can try deleting the cache manually.</source>
|
<source>Migrating the cache to the current version failed. This can have different reasons. Please open an issue at https://github.com/Nheko-Reborn/nheko and try to use an older version in the meantime. Alternatively you can try deleting the cache manually.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>La migration du cache vers la version actuelle a échoué. Plusieurs causes sont possibles. Merci d'ouvrir un rapport d'anomalie sur https://github.com/Nheko-Reborn/nheko et essayez d'utiliser une version antérieure entretemps. Vous pouvez également tenter d'effacer le cache manuellement.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+12"/>
|
<location line="+12"/>
|
||||||
|
@ -371,7 +373,8 @@ If you think this is a mistake, you can close Nheko instead to possibly recover
|
||||||
<location line="+34"/>
|
<location line="+34"/>
|
||||||
<source>You failed to join %1. You can try to knock so that others can invite you in. Do you want to do so?
|
<source>You failed to join %1. You can try to knock so that others can invite you in. Do you want to do so?
|
||||||
You may optionally provide a reason for others to accept your knock:</source>
|
You may optionally provide a reason for others to accept your knock:</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Vous n'avez pas pu rejoindre %1. Vous pouvez essayer de frapper au salon afin que les autres membres vous invitent. Voulez-vous le faire ?
|
||||||
|
Vous pouvez éventuellement fournir une raison afin que les membres acceptent votre requête :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+52"/>
|
<location line="+52"/>
|
||||||
|
@ -381,7 +384,7 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
<message>
|
<message>
|
||||||
<location line="+9"/>
|
<location line="+9"/>
|
||||||
<source>Failed to remove invite: %1</source>
|
<source>Failed to remove invite: %1</source>
|
||||||
<translation>Impossible de supprimer l'invitation : %1</translation>
|
<translation>Impossible de supprimer l'invitation : %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+27"/>
|
<location line="+27"/>
|
||||||
|
@ -406,7 +409,7 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
<message>
|
<message>
|
||||||
<location line="+15"/>
|
<location line="+15"/>
|
||||||
<source>Failed to kick %1 from %2: %3</source>
|
<source>Failed to kick %1 from %2: %3</source>
|
||||||
<translation>Échec de l'expulsion de %1 de %2  : %3</translation>
|
<translation>Échec de l'expulsion de %1 de %2 : %3</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+13"/>
|
<location line="+13"/>
|
||||||
|
@ -429,27 +432,27 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../src/CommandCompleter.cpp" line="+106"/>
|
<location filename="../../src/CommandCompleter.cpp" line="+106"/>
|
||||||
<source>/me <message></source>
|
<source>/me <message></source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>/me <message></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>/react <text></source>
|
<source>/react <text></source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>/react <texte></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+6"/>
|
<location line="+6"/>
|
||||||
<source>/part [reason]</source>
|
<source>/part [reason]</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>/part [raison]</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>/leave [reason]</source>
|
<source>/leave [reason]</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>/leave [raison]</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+12"/>
|
<location line="+12"/>
|
||||||
<source>/roomnick <displayname></source>
|
<source>/roomnick <displayname></source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>/roomnick <nomaffiché></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
|
@ -565,57 +568,57 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>Leave a room. Reason is optional.</source>
|
<source>Leave a room. Reason is optional.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Quitte un salon. La raison est optionnelle.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>Invite a user into the current room. Reason is optional.</source>
|
<source>Invite a user into the current room. Reason is optional.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Invite un utilisateur dans le salon actuel. La raison est optionnelle.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>Kick a user from the current room. Reason is optional.</source>
|
<source>Kick a user from the current room. Reason is optional.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Expulse un utilisateur du salon actuel. La raison est optionnelle.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>Ban a user from the current room. Reason is optional.</source>
|
<source>Ban a user from the current room. Reason is optional.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Bannit un utilisateur du salon actuel. La raison est optionnelle.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>Unban a user in the current room. Reason is optional.</source>
|
<source>Unban a user in the current room. Reason is optional.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Dé-bannit un utilisateur du salon actuel. La raison est optionnelle.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>Redact an event or all locally cached messages of a user.</source>
|
<source>Redact an event or all locally cached messages of a user.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Efface un évènement ou tous les messages connus (présents dans la base de données locale) d'un utilisateur.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>Change your displayname in this room.</source>
|
<source>Change your displayname in this room.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Change votre nom affiché dans ce salon.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>¯\_(ツ)_/¯ with an optional message.</source>
|
<source>¯\_(ツ)_/¯ with an optional message.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>¯\_(ツ)_/¯ avec un message optionnel.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>(╯°□°)╯︵ ┻━┻</source>
|
<source>(╯°□°)╯︵ ┻━┻</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>(╯°□°)╯︵ ┻━┻</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>┯━┯╭( º _ º╭)</source>
|
<source>┯━┯╭( º _ º╭)</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>┯━┯╭( º _ º╭)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>ノ┬─┬ノ ︵ ( \o°o)\</source>
|
<source>ノ┬─┬ノ ︵ ( \o°o)\</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>ノ┬─┬ノ ︵ ( \o°o)\</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
|
@ -650,32 +653,32 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>Send a message in rainbow colors.</source>
|
<source>Send a message in rainbow colors.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Envoie un message aux couleurs de l'arc-en-ciel.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>Send /me in rainbow colors.</source>
|
<source>Send /me in rainbow colors.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Envoie /me aux couleurs de l'arc-en-ciel.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>Send a bot message.</source>
|
<source>Send a bot message.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Envoie un message de robot.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>Send a bot message in rainbow colors.</source>
|
<source>Send a bot message in rainbow colors.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Envoie un message de robot aux couleurs de l'arc-en-ciel.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>Send a message with confetti.</source>
|
<source>Send a message with confetti.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Envoie un message avec des confettis.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>Send a message in rainbow colors with confetti.</source>
|
<source>Send a message in rainbow colors with confetti.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Envoie des confettis avec un message aux couleurs de l'arc-en-ciel.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
|
@ -698,12 +701,12 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/CommunitiesList.qml" line="+65"/>
|
<location filename="../qml/CommunitiesList.qml" line="+65"/>
|
||||||
<source>Do not show notification counts for this community or tag.</source>
|
<source>Do not show notification counts for this community or tag.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Ne pas afficher le compteur de notifications pour cette communauté ou cette étiquette.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+7"/>
|
<location line="+7"/>
|
||||||
<source>Hide rooms with this tag or from this community by default.</source>
|
<source>Hide rooms with this tag or from this community by default.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Cache par défaut les salons avec cette étiquette ou provenant de cette communauté.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+86"/>
|
<location line="+86"/>
|
||||||
|
@ -771,7 +774,7 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
<message>
|
<message>
|
||||||
<location line="+660"/>
|
<location line="+660"/>
|
||||||
<source>Failed to update community: %1</source>
|
<source>Failed to update community: %1</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Erreur lors de la mise à jour de cette communauté : %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+18"/>
|
<location line="+18"/>
|
||||||
|
@ -804,7 +807,7 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
<location line="+67"/>
|
<location line="+67"/>
|
||||||
<source>%n member(s)</source>
|
<source>%n member(s)</source>
|
||||||
<translation type="unfinished">
|
<translation>
|
||||||
<numerusform>%n membre</numerusform>
|
<numerusform>%n membre</numerusform>
|
||||||
<numerusform>%n membres</numerusform>
|
<numerusform>%n membres</numerusform>
|
||||||
</translation>
|
</translation>
|
||||||
|
@ -964,17 +967,17 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
<message>
|
<message>
|
||||||
<location line="+8"/>
|
<location line="+8"/>
|
||||||
<source>Please verify the following digits. You should see the same numbers on both sides. If they differ, please press 'They do not match!' to abort verification!</source>
|
<source>Please verify the following digits. You should see the same numbers on both sides. If they differ, please press 'They do not match!' to abort verification!</source>
|
||||||
<translation>Veuillez vérifier les chiffres suivants. Vous devriez voir les mêmes chiffres des deux côtés. Si ceux-ci diffèrent, veuillez choisir « Ils sont différents ! » pour annuler la vérification !</translation>
|
<translation>Veuillez vérifier les chiffres suivants. Vous devriez voir les mêmes chiffres des deux côtés. Si ceux-ci diffèrent, veuillez choisir « Ils sont différents ! » pour annuler la vérification !</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+33"/>
|
<location line="+33"/>
|
||||||
<source>They do not match!</source>
|
<source>They do not match!</source>
|
||||||
<translation>Ils sont différents !</translation>
|
<translation>Ils sont différents !</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+13"/>
|
<location line="+13"/>
|
||||||
<source>They match!</source>
|
<source>They match!</source>
|
||||||
<translation>Ils sont identiques !</translation>
|
<translation>Ils sont identiques !</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -1035,7 +1038,7 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
<message>
|
<message>
|
||||||
<location line="+8"/>
|
<location line="+8"/>
|
||||||
<source>Please verify the following emoji. You should see the same emoji on both sides. If they differ, please press 'They do not match!' to abort verification!</source>
|
<source>Please verify the following emoji. You should see the same emoji on both sides. If they differ, please press 'They do not match!' to abort verification!</source>
|
||||||
<translation>Veuillez vérifier les émoji suivants. Vous devriez voir les mêmes émoji des deux côtés. S'ils diffèrent, veuillez choisir « Ils sont différents ! » pour annuler la vérification !</translation>
|
<translation>Veuillez vérifier les émoji suivants. Vous devriez voir les mêmes émoji des deux côtés. S'ils diffèrent, veuillez choisir « Ils sont différents ! » pour annuler la vérification !</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+379"/>
|
<location line="+379"/>
|
||||||
|
@ -1045,12 +1048,12 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
<message>
|
<message>
|
||||||
<location line="+10"/>
|
<location line="+10"/>
|
||||||
<source>They do not match!</source>
|
<source>They do not match!</source>
|
||||||
<translation>Ils sont différents !</translation>
|
<translation>Ils sont différents !</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+13"/>
|
<location line="+13"/>
|
||||||
<source>They match!</source>
|
<source>They match!</source>
|
||||||
<translation>Ils sont identiques !</translation>
|
<translation>Ils sont identiques !</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -1114,7 +1117,7 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/EncryptionIndicator.qml" line="+57"/>
|
<location filename="../qml/EncryptionIndicator.qml" line="+57"/>
|
||||||
<source>This message is not encrypted!</source>
|
<source>This message is not encrypted!</source>
|
||||||
<translation>Ce message n'est pas chiffré !</translation>
|
<translation>Ce message n'est pas chiffré !</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+4"/>
|
<location line="+4"/>
|
||||||
|
@ -1147,7 +1150,7 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
<message>
|
<message>
|
||||||
<location line="+4"/>
|
<location line="+4"/>
|
||||||
<source>Key mismatch detected!</source>
|
<source>Key mismatch detected!</source>
|
||||||
<translation>Clés non correspondantes détectées !</translation>
|
<translation>Clés non correspondantes détectées !</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
|
@ -1162,7 +1165,7 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>Verification messages received out of order!</source>
|
<source>Verification messages received out of order!</source>
|
||||||
<translation>Messages de vérification reçus dans le désordre !</translation>
|
<translation>Messages de vérification reçus dans le désordre !</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
|
@ -1390,11 +1393,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation>Sélectionnez un fichier</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
@ -1543,7 +1541,7 @@ Example: https://server.my:8787</source>
|
||||||
<location line="+64"/>
|
<location line="+64"/>
|
||||||
<location line="+127"/>
|
<location line="+127"/>
|
||||||
<source>You have entered an invalid Matrix ID e.g @joe:matrix.org</source>
|
<source>You have entered an invalid Matrix ID e.g @joe:matrix.org</source>
|
||||||
<translation>Vous avez entré un identifiant Matrix invalide exemple correct : @moi:monserveur.example.com)</translation>
|
<translation>Vous avez entré un identifiant Matrix invalide exemple correct : @moi:monserveur.example.com)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="-157"/>
|
<location line="-157"/>
|
||||||
|
@ -1626,12 +1624,12 @@ Example: https://server.my:8787</source>
|
||||||
<message>
|
<message>
|
||||||
<location line="+1"/>
|
<location line="+1"/>
|
||||||
<source>A call is in progress. Log out?</source>
|
<source>A call is in progress. Log out?</source>
|
||||||
<translation>Un appel est en cours. Se déconnecter ?</translation>
|
<translation>Un appel est en cours. Se déconnecter ?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>Are you sure you want to log out?</source>
|
<source>Are you sure you want to log out?</source>
|
||||||
<translation>Êtes-vous certain de vouloir vous déconnecter ?</translation>
|
<translation>Êtes-vous certain de vouloir vous déconnecter ?</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -2041,7 +2039,7 @@ Example: https://server.my:8787</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/voip/PlaceCall.qml" line="+42"/>
|
<location filename="../qml/voip/PlaceCall.qml" line="+42"/>
|
||||||
<source>Place a call to %1?</source>
|
<source>Place a call to %1?</source>
|
||||||
<translation>Appeler %1 ?</translation>
|
<translation>Appeler %1 ?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+16"/>
|
<location line="+16"/>
|
||||||
|
@ -3174,17 +3172,17 @@ Veuillez noter qu'il ne pourra plus être désactivé par la suite.</transl
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/voip/ScreenShare.qml" line="+30"/>
|
<location filename="../qml/voip/ScreenShare.qml" line="+30"/>
|
||||||
<source>Share desktop with %1?</source>
|
<source>Share desktop with %1?</source>
|
||||||
<translation>Partager le bureau avec %1  ?</translation>
|
<translation>Partager le bureau avec %1 ?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+11"/>
|
<location line="+11"/>
|
||||||
<source>Window:</source>
|
<source>Window:</source>
|
||||||
<translation>Fenêtre :</translation>
|
<translation>Fenêtre :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+20"/>
|
<location line="+20"/>
|
||||||
<source>Frame rate:</source>
|
<source>Frame rate:</source>
|
||||||
<translation>Fréquence d'images :</translation>
|
<translation>Fréquence d'images :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+19"/>
|
<location line="+19"/>
|
||||||
|
@ -3233,7 +3231,7 @@ Veuillez noter qu'il ne pourra plus être désactivé par la suite.</transl
|
||||||
<message>
|
<message>
|
||||||
<location line="+1"/>
|
<location line="+1"/>
|
||||||
<source>Nheko could not connect to the secure storage to save encryption secrets to. This can have multiple reasons. Check if your D-Bus service is running and you have configured a service like KWallet, Gnome Keyring, KeePassXC or the equivalent for your platform. If you are having trouble, feel free to open an issue here: https://github.com/Nheko-Reborn/nheko/issues</source>
|
<source>Nheko could not connect to the secure storage to save encryption secrets to. This can have multiple reasons. Check if your D-Bus service is running and you have configured a service like KWallet, Gnome Keyring, KeePassXC or the equivalent for your platform. If you are having trouble, feel free to open an issue here: https://github.com/Nheko-Reborn/nheko/issues</source>
|
||||||
<translation>Nheko n'a pas pu se connecter au stockage sécurisé afin d'y sauvegarder les clés de chiffrement. Cela peut avoir différentes causes. Vérifiez si votre service D-Bus est lancé, et si vous avez configuré un service tel que KWallet ; Gnome Keyring ; KeePassXC ou l'équivalent pour votre système. Si vous n'arrivez pas à résoudre le problème, n'hésitez pas à nous en faire part ici : https ://github.com/Nheko-Reborn/nheko/issues</translation>
|
<translation>Nheko n'a pas pu se connecter au stockage sécurisé afin d'y sauvegarder les clés de chiffrement. Cela peut avoir différentes causes. Vérifiez si votre service D-Bus est lancé, et si vous avez configuré un service tel que KWallet ; Gnome Keyring ; KeePassXC ou l'équivalent pour votre système. Si vous n'arrivez pas à résoudre le problème, n'hésitez pas à nous en faire part ici : https ://github.com/Nheko-Reborn/nheko/issues</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -3241,7 +3239,7 @@ Veuillez noter qu'il ne pourra plus être désactivé par la suite.</transl
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/SelfVerificationCheck.qml" line="+41"/>
|
<location filename="../qml/SelfVerificationCheck.qml" line="+41"/>
|
||||||
<source>This is your recovery key. You will need it to restore access to your encrypted messages and verification keys. Keep this safe. Don't share it with anyone and don't lose it! Do not pass go! Do not collect $200!</source>
|
<source>This is your recovery key. You will need it to restore access to your encrypted messages and verification keys. Keep this safe. Don't share it with anyone and don't lose it! Do not pass go! Do not collect $200!</source>
|
||||||
<translation>Ceci est votre clé de récupération. Vous en aurez besoin afin de restaurer l'accès à vos messages chiffrés et à vos clés de vérification. Gardez cette clé en sûreté. Ne la partagez pas avec qui que ce soit et ne la perdez pas ! Ne passez pas par la case départ et ne recevez pas 20 000 francs !</translation>
|
<translation>Ceci est votre clé de récupération. Vous en aurez besoin afin de restaurer l'accès à vos messages chiffrés et à vos clés de vérification. Gardez cette clé en sûreté. Ne la partagez pas avec qui que ce soit et ne la perdez pas ! Ne passez pas par la case départ et ne recevez pas 20 000 francs !</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+33"/>
|
<location line="+33"/>
|
||||||
|
@ -3262,8 +3260,8 @@ Veuillez noter qu'il ne pourra plus être désactivé par la suite.</transl
|
||||||
<location line="+10"/>
|
<location line="+10"/>
|
||||||
<source>Hello and welcome to Matrix!
|
<source>Hello and welcome to Matrix!
|
||||||
It seems like you are new. Before you can securely encrypt your messages, we need to setup a few small things. You can either press accept immediately or adjust a few basic options. We also try to explain a few of the basics. You can skip those parts, but they might prove to be helpful!</source>
|
It seems like you are new. Before you can securely encrypt your messages, we need to setup a few small things. You can either press accept immediately or adjust a few basic options. We also try to explain a few of the basics. You can skip those parts, but they might prove to be helpful!</source>
|
||||||
<translation>Bonjour et bienvenue sur le réseau Matrix !
|
<translation>Bonjour et bienvenue sur le réseau Matrix !
|
||||||
Il semblerait que ce soit votre première fois ici. Avant de pouvoir chiffrer vos messages de manière sécurisée, nous devons configurer quelques détails. Vous pouvez soit accepter immédiatement, soit ajuster quelques options basiques. Nous essayons également d'expliquer le fonctionnement de certains mécanismes. Vous pouvez sauter ces étapes, mais celles-ci pourraient se montrer utiles par la suite !</translation>
|
Il semblerait que ce soit votre première fois ici. Avant de pouvoir chiffrer vos messages de manière sécurisée, nous devons configurer quelques détails. Vous pouvez soit accepter immédiatement, soit ajuster quelques options basiques. Nous essayons également d'expliquer le fonctionnement de certains mécanismes. Vous pouvez sauter ces étapes, mais celles-ci pourraient se montrer utiles par la suite !</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+124"/>
|
<location line="+124"/>
|
||||||
|
@ -3293,17 +3291,17 @@ Si vous choisissez de vérifier, vous aurez besoin de l'autre appareil. Si
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../src/encryption/SelfVerificationStatus.cpp" line="+47"/>
|
<location filename="../../src/encryption/SelfVerificationStatus.cpp" line="+47"/>
|
||||||
<source>Failed to create keys for cross-signing!</source>
|
<source>Failed to create keys for cross-signing!</source>
|
||||||
<translation>Échec de la création des clés pour l'auto-vérification (cross-signing) !</translation>
|
<translation>Échec de la création des clés pour l'auto-vérification (cross-signing) !</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+16"/>
|
<location line="+16"/>
|
||||||
<source>Failed to create keys for online key backup!</source>
|
<source>Failed to create keys for online key backup!</source>
|
||||||
<translation>Échec de la création de clés pour la sauvegarde en ligne !</translation>
|
<translation>Échec de la création de clés pour la sauvegarde en ligne !</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+29"/>
|
<location line="+29"/>
|
||||||
<source>Failed to create keys for secure server side secret storage!</source>
|
<source>Failed to create keys for secure server side secret storage!</source>
|
||||||
<translation>Échec de la création des clés pour le stockage sécurisé côté serveur !</translation>
|
<translation>Échec de la création des clés pour le stockage sécurisé côté serveur !</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+44"/>
|
<location line="+44"/>
|
||||||
|
@ -3313,7 +3311,7 @@ Si vous choisissez de vérifier, vous aurez besoin de l'autre appareil. Si
|
||||||
<message>
|
<message>
|
||||||
<location line="+6"/>
|
<location line="+6"/>
|
||||||
<source>Encryption setup failed: %1</source>
|
<source>Encryption setup failed: %1</source>
|
||||||
<translation>Échec de la configuration du chiffrement : %1</translation>
|
<translation>Échec de la configuration du chiffrement : %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+156"/>
|
<location line="+156"/>
|
||||||
|
@ -3426,7 +3424,7 @@ Si vous choisissez de vérifier, vous aurez besoin de l'autre appareil. Si
|
||||||
<message>
|
<message>
|
||||||
<location line="+10"/>
|
<location line="+10"/>
|
||||||
<source>Verification successful! Both sides verified their devices!</source>
|
<source>Verification successful! Both sides verified their devices!</source>
|
||||||
<translation>Vérification réussie ! Les deux côtés ont vérifié leur appareil !</translation>
|
<translation>Vérification réussie ! Les deux côtés ont vérifié leur appareil !</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+14"/>
|
<location line="+14"/>
|
||||||
|
@ -3446,7 +3444,7 @@ Si vous choisissez de vérifier, vous aurez besoin de l'autre appareil. Si
|
||||||
<location line="+115"/>
|
<location line="+115"/>
|
||||||
<location line="+5"/>
|
<location line="+5"/>
|
||||||
<source>Failed to encrypt event, sending aborted!</source>
|
<source>Failed to encrypt event, sending aborted!</source>
|
||||||
<translation>Échec du chiffrement de l'évènement, envoi abandonné !</translation>
|
<translation>Échec du chiffrement de l'évènement, envoi abandonné !</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+196"/>
|
<location line="+196"/>
|
||||||
|
@ -3826,7 +3824,7 @@ Raison : %4</translation>
|
||||||
<location line="+36"/>
|
<location line="+36"/>
|
||||||
<source>%1 left after having already left!</source>
|
<source>%1 left after having already left!</source>
|
||||||
<comment>This is a leave event after the user already left and shouldn't happen apart from state resets</comment>
|
<comment>This is a leave event after the user already left and shouldn't happen apart from state resets</comment>
|
||||||
<translation>%1 a quitté le salon après l'avoir déjà quitté !</translation>
|
<translation>%1 a quitté le salon après l'avoir déjà quitté !</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+7"/>
|
<location line="+7"/>
|
||||||
|
@ -4863,17 +4861,17 @@ This setting will take effect upon restart.</source>
|
||||||
<message>
|
<message>
|
||||||
<location line="+12"/>
|
<location line="+12"/>
|
||||||
<source>Waiting for other side to accept the verification request.</source>
|
<source>Waiting for other side to accept the verification request.</source>
|
||||||
<translation>Attente que le correspondant accepte la demande de vérification.</translation>
|
<translation>Attente d'acceptation de la demande de vérification par le correspondant.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>Waiting for other side to continue the verification process.</source>
|
<source>Waiting for other side to continue the verification process.</source>
|
||||||
<translation>Attente que le correspondant poursuive le processus de vérification.</translation>
|
<translation>Attente de la poursuite du processus de vérification par le correspondant.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>Waiting for other side to complete the verification process.</source>
|
<source>Waiting for other side to complete the verification process.</source>
|
||||||
<translation>Attente que le correspondant termine le processus de vérification.</translation>
|
<translation>Attente de la fin du processus de vérification par le correspondant.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+19"/>
|
<location line="+19"/>
|
||||||
|
|
|
@ -1388,11 +1388,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation>Fájl kiválasztása</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1391,11 +1391,6 @@ Kamu dapat memberikan alasan untuk orang lain untuk menerima ketukanmu:</transla
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation>Pilih sebuah file</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
@ -1406,6 +1401,11 @@ Kamu dapat memberikan alasan untuk orang lain untuk menerima ketukanmu:</transla
|
||||||
<source>Upload of '%1' failed</source>
|
<source>Upload of '%1' failed</source>
|
||||||
<translation>Pengunggahan '%1' gagal</translation>
|
<translation>Pengunggahan '%1' gagal</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
||||||
|
<source>Select file(s)</source>
|
||||||
|
<translation>Pilih berkas</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InviteDialog</name>
|
<name>InviteDialog</name>
|
||||||
|
|
|
@ -1390,11 +1390,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1390,11 +1390,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation type="unfinished">Seleziona un file</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1388,11 +1388,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation type="unfinished">ファイルを選択</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1390,11 +1390,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation>ഒരു ഫയൽ തിരഞ്ഞെടുക്കുക</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1393,11 +1393,6 @@ Je kan optioneel hier een reden invoeren dat je aanklopt:</translation>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation>Selecteer een bestand</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1392,11 +1392,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation>Wybierz plik</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1390,11 +1390,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1390,11 +1390,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation>Selecionar um ficheiro</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1392,11 +1392,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1392,11 +1392,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation>Выберите файл</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1390,11 +1390,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1392,11 +1392,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1390,11 +1390,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation>Välj en fil</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1393,11 +1393,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1395,11 +1395,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation>Виберіть файл</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1388,11 +1388,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -1391,11 +1391,6 @@ You may optionally provide a reason for others to accept your knock:</source>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>InputBar</name>
|
<name>InputBar</name>
|
||||||
<message>
|
|
||||||
<location filename="../../src/timeline/InputBar.cpp" line="+372"/>
|
|
||||||
<source>Select a file</source>
|
|
||||||
<translation>选择一个文件</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<source>All Files (*)</source>
|
<source>All Files (*)</source>
|
||||||
|
|
|
@ -11,78 +11,67 @@ import im.nheko 1.0
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: avatar
|
id: avatar
|
||||||
|
|
||||||
|
property alias color: bg.color
|
||||||
|
property bool crop: true
|
||||||
|
property string displayName
|
||||||
|
property string roomid
|
||||||
|
property alias textColor: label.color
|
||||||
property string url
|
property string url
|
||||||
property string userid
|
property string userid
|
||||||
property string roomid
|
|
||||||
property string displayName
|
|
||||||
property alias textColor: label.color
|
|
||||||
property bool crop: true
|
|
||||||
property alias color: bg.color
|
|
||||||
|
|
||||||
width: 48
|
|
||||||
height: 48
|
height: 48
|
||||||
|
width: 48
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
id: bg
|
id: bg
|
||||||
|
|
||||||
|
color: palette.alternateBase
|
||||||
radius: Settings.avatarCircles ? height / 2 : height / 8
|
radius: Settings.avatarCircles ? height / 2 : height / 8
|
||||||
color: Nheko.colors.alternateBase
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: label
|
id: label
|
||||||
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
text: TimelineManager.escapeEmoji(displayName ? String.fromCodePoint(displayName.codePointAt(0)) : "")
|
color: palette.text
|
||||||
textFormat: Text.RichText
|
enabled: false
|
||||||
font.pixelSize: avatar.height / 2
|
font.pixelSize: avatar.height / 2
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: TimelineManager.escapeEmoji(avatar.displayName ? String.fromCodePoint(avatar.displayName.codePointAt(0)) : "")
|
||||||
|
textFormat: Text.RichText
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
visible: img.status != Image.Ready && !Settings.useIdenticon
|
visible: img.status != Image.Ready && !Settings.useIdenticon
|
||||||
color: Nheko.colors.text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: identicon
|
id: identicon
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
source: Settings.useIdenticon ? ("image://jdenticon/" + (avatar.userid !== "" ? avatar.userid : avatar.roomid) + "?radius=" + (Settings.avatarCircles ? 100 : 25)) : ""
|
||||||
visible: Settings.useIdenticon && img.status != Image.Ready
|
visible: Settings.useIdenticon && img.status != Image.Ready
|
||||||
source: Settings.useIdenticon ? ("image://jdenticon/" + (userid !== "" ? userid : roomid) + "?radius=" + (Settings.avatarCircles ? 100 : 25)) : ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: img
|
id: img
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
fillMode: avatar.crop ? Image.PreserveAspectCrop : Image.PreserveAspectFit
|
fillMode: avatar.crop ? Image.PreserveAspectCrop : Image.PreserveAspectFit
|
||||||
mipmap: true
|
source: if (avatar.url.startsWith('image://colorimage')) {
|
||||||
smooth: true
|
return avatar.url + "&radius=" + (Settings.avatarCircles ? 100 : 25) + ((avatar.crop) ? "" : "&scale");
|
||||||
sourceSize.width: avatar.width * Screen.devicePixelRatio
|
} else if (avatar.url.startsWith('image://')) {
|
||||||
sourceSize.height: avatar.height * Screen.devicePixelRatio
|
|
||||||
source: if (avatar.url.startsWith('image://')) {
|
|
||||||
return avatar.url + "?radius=" + (Settings.avatarCircles ? 100 : 25) + ((avatar.crop) ? "" : "&scale");
|
return avatar.url + "?radius=" + (Settings.avatarCircles ? 100 : 25) + ((avatar.crop) ? "" : "&scale");
|
||||||
} else if (avatar.url.startsWith(':/')) {
|
} else if (avatar.url.startsWith(':/')) {
|
||||||
return "image://colorimage/" + avatar.url + "?" + textColor;
|
return "image://colorimage/" + avatar.url + "?" + label.color;
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
sourceSize.height: avatar.height * Screen.devicePixelRatio
|
||||||
|
sourceSize.width: avatar.width * Screen.devicePixelRatio
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: onlineIndicator
|
id: onlineIndicator
|
||||||
|
|
||||||
anchors.bottom: avatar.bottom
|
|
||||||
anchors.right: avatar.right
|
|
||||||
visible: !!userid
|
|
||||||
height: avatar.height / 6
|
|
||||||
width: height
|
|
||||||
radius: Settings.avatarCircles ? height / 2 : height / 8
|
|
||||||
color: updatePresence()
|
|
||||||
|
|
||||||
function updatePresence() {
|
function updatePresence() {
|
||||||
switch (Presence.userPresence(userid)) {
|
switch (Presence.userPresence(avatar.userid)) {
|
||||||
case "online":
|
case "online":
|
||||||
return Nheko.theme.online;
|
return Nheko.theme.online;
|
||||||
case "unavailable":
|
case "unavailable":
|
||||||
|
@ -94,22 +83,28 @@ AbstractButton {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
anchors.bottom: avatar.bottom
|
||||||
target: Presence
|
anchors.right: avatar.right
|
||||||
|
color: updatePresence()
|
||||||
|
height: avatar.height / 6
|
||||||
|
radius: Settings.avatarCircles ? height / 2 : height / 8
|
||||||
|
visible: !!avatar.userid
|
||||||
|
width: height
|
||||||
|
|
||||||
|
Connections {
|
||||||
function onPresenceChanged(id) {
|
function onPresenceChanged(id) {
|
||||||
if (id == userid) onlineIndicator.color = onlineIndicator.updatePresence();
|
if (id == avatar.userid)
|
||||||
|
onlineIndicator.color = onlineIndicator.updatePresence();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target: Presence
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
NhekoCursorShape {
|
||||||
CursorShape {
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
Ripple {
|
Ripple {
|
||||||
color: Qt.rgba(Nheko.colors.alternateBase.r, Nheko.colors.alternateBase.g, Nheko.colors.alternateBase.b, 0.5)
|
color: Qt.rgba(palette.alternateBase.r, palette.alternateBase.g, palette.alternateBase.b, 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,19 +14,19 @@ import QtQml 2.15
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: chatPage
|
id: chatPage
|
||||||
|
|
||||||
color: Nheko.colors.window
|
color: palette.window
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 0
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: offlineIndicator
|
id: offlineIndicator
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: offlineLabel.height + Nheko.paddingMedium
|
||||||
color: Nheko.theme.error
|
color: Nheko.theme.error
|
||||||
visible: !TimelineManager.isConnected
|
visible: !TimelineManager.isConnected
|
||||||
Layout.preferredHeight: offlineLabel.height + Nheko.paddingMedium
|
|
||||||
Layout.fillWidth: true
|
|
||||||
z: 1
|
z: 1
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
@ -36,18 +36,9 @@ Rectangle {
|
||||||
text: qsTr("No network connection")
|
text: qsTr("No network connection")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AdaptiveLayout {
|
AdaptiveLayout {
|
||||||
id: adaptiveView
|
id: adaptiveView
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
singlePageMode: communityListC.preferredWidth + roomListC.preferredWidth + timlineViewC.minimumWidth > width
|
|
||||||
pageIndex: 1
|
|
||||||
|
|
||||||
Component.onCompleted: initializePageIndex()
|
|
||||||
onSinglePageModeChanged: initializePageIndex()
|
|
||||||
|
|
||||||
function initializePageIndex() {
|
function initializePageIndex() {
|
||||||
if (!singlePageMode)
|
if (!singlePageMode)
|
||||||
adaptiveView.pageIndex = 0;
|
adaptiveView.pageIndex = 0;
|
||||||
|
@ -57,67 +48,67 @@ Rectangle {
|
||||||
adaptiveView.pageIndex = 1;
|
adaptiveView.pageIndex = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
pageIndex: 1
|
||||||
|
singlePageMode: communityListC.preferredWidth + roomListC.preferredWidth + timlineViewC.minimumWidth > width
|
||||||
|
|
||||||
|
Component.onCompleted: initializePageIndex()
|
||||||
|
onSinglePageModeChanged: initializePageIndex()
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Rooms
|
|
||||||
function onCurrentRoomChanged() {
|
function onCurrentRoomChanged() {
|
||||||
adaptiveView.initializePageIndex();
|
adaptiveView.initializePageIndex();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
target: Rooms
|
||||||
|
}
|
||||||
AdaptiveLayoutElement {
|
AdaptiveLayoutElement {
|
||||||
id: communityListC
|
id: communityListC
|
||||||
|
|
||||||
visible: Settings.groupView
|
|
||||||
minimumWidth: communitiesList.avatarSize * 4 + Nheko.paddingMedium * 2
|
|
||||||
collapsedWidth: communitiesList.avatarSize + 2 * Nheko.paddingMedium
|
collapsedWidth: communitiesList.avatarSize + 2 * Nheko.paddingMedium
|
||||||
preferredWidth: Settings.communityListWidth >= minimumWidth ? Settings.communityListWidth : collapsedWidth
|
|
||||||
maximumWidth: communitiesList.avatarSize * 10 + 2 * Nheko.paddingMedium
|
maximumWidth: communitiesList.avatarSize * 10 + 2 * Nheko.paddingMedium
|
||||||
|
minimumWidth: communitiesList.avatarSize * 4 + Nheko.paddingMedium * 2
|
||||||
|
preferredWidth: Settings.communityListWidth >= minimumWidth ? Settings.communityListWidth : collapsedWidth
|
||||||
|
visible: Settings.groupView
|
||||||
|
|
||||||
CommunitiesList {
|
CommunitiesList {
|
||||||
id: communitiesList
|
id: communitiesList
|
||||||
|
|
||||||
collapsed: parent.collapsed
|
collapsed: parent.collapsed
|
||||||
}
|
}
|
||||||
|
|
||||||
Binding {
|
Binding {
|
||||||
target: Settings
|
delayed: true
|
||||||
property: 'communityListWidth'
|
property: 'communityListWidth'
|
||||||
|
restoreMode: Binding.RestoreBindingOrValue
|
||||||
|
target: Settings
|
||||||
value: communityListC.preferredWidth
|
value: communityListC.preferredWidth
|
||||||
when: !adaptiveView.singlePageMode
|
when: !adaptiveView.singlePageMode
|
||||||
delayed: true
|
|
||||||
restoreMode: Binding.RestoreBindingOrValue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AdaptiveLayoutElement {
|
AdaptiveLayoutElement {
|
||||||
id: roomListC
|
id: roomListC
|
||||||
|
|
||||||
minimumWidth: roomlist.avatarSize * 4 + Nheko.paddingSmall * 2
|
|
||||||
preferredWidth: (Settings.roomListWidth == - 1)
|
|
||||||
? (roomlist.avatarSize * 5 + Nheko.paddingSmall * 2)
|
|
||||||
: (Settings.roomListWidth >= minimumWidth ? Settings.roomListWidth : collapsedWidth)
|
|
||||||
maximumWidth: roomlist.avatarSize * 10 + Nheko.paddingSmall * 2
|
|
||||||
collapsedWidth: roomlist.avatarSize + 2 * Nheko.paddingMedium
|
collapsedWidth: roomlist.avatarSize + 2 * Nheko.paddingMedium
|
||||||
|
maximumWidth: roomlist.avatarSize * 10 + Nheko.paddingSmall * 2
|
||||||
|
minimumWidth: roomlist.avatarSize * 4 + Nheko.paddingSmall * 2
|
||||||
|
preferredWidth: (Settings.roomListWidth == -1) ? (roomlist.avatarSize * 5 + Nheko.paddingSmall * 2) : (Settings.roomListWidth >= minimumWidth ? Settings.roomListWidth : collapsedWidth)
|
||||||
|
|
||||||
RoomList {
|
RoomList {
|
||||||
id: roomlist
|
id: roomlist
|
||||||
|
|
||||||
height: adaptiveView.height
|
|
||||||
collapsed: parent.collapsed
|
collapsed: parent.collapsed
|
||||||
|
height: adaptiveView.height
|
||||||
}
|
}
|
||||||
|
|
||||||
Binding {
|
Binding {
|
||||||
target: Settings
|
delayed: true
|
||||||
property: 'roomListWidth'
|
property: 'roomListWidth'
|
||||||
|
restoreMode: Binding.RestoreBindingOrValue
|
||||||
|
target: Settings
|
||||||
value: roomListC.preferredWidth
|
value: roomListC.preferredWidth
|
||||||
when: !adaptiveView.singlePageMode
|
when: !adaptiveView.singlePageMode
|
||||||
delayed: true
|
|
||||||
restoreMode: Binding.RestoreBindingOrValue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AdaptiveLayoutElement {
|
AdaptiveLayoutElement {
|
||||||
id: timlineViewC
|
id: timlineViewC
|
||||||
|
|
||||||
|
@ -127,25 +118,20 @@ Rectangle {
|
||||||
id: timeline
|
id: timeline
|
||||||
|
|
||||||
privacyScreen: privacyScreen
|
privacyScreen: privacyScreen
|
||||||
showBackButton: adaptiveView.singlePageMode
|
|
||||||
room: Rooms.currentRoom
|
room: Rooms.currentRoom
|
||||||
roomPreview: Rooms.currentRoomPreview.roomid ? Rooms.currentRoomPreview : null
|
roomPreview: Rooms.currentRoomPreview.roomid ? Rooms.currentRoomPreview : null
|
||||||
|
showBackButton: adaptiveView.singlePageMode
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PrivacyScreen {
|
PrivacyScreen {
|
||||||
id: privacyScreen
|
id: privacyScreen
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: Settings.privacyScreen
|
|
||||||
screenTimeout: Settings.privacyScreenTimeout
|
screenTimeout: Settings.privacyScreenTimeout
|
||||||
timelineRoot: adaptiveView
|
timelineRoot: adaptiveView
|
||||||
|
visible: Settings.privacyScreen
|
||||||
windowTarget: MainWindow
|
windowTarget: MainWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,19 +13,24 @@ import im.nheko 1.0
|
||||||
|
|
||||||
Page {
|
Page {
|
||||||
id: communitySidebar
|
id: communitySidebar
|
||||||
|
|
||||||
//leftPadding: Nheko.paddingSmall
|
//leftPadding: Nheko.paddingSmall
|
||||||
//rightPadding: Nheko.paddingSmall
|
//rightPadding: Nheko.paddingSmall
|
||||||
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 1.6)
|
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 1.6)
|
||||||
property bool collapsed: false
|
property bool collapsed: false
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: Nheko.theme.sidebarBackground
|
||||||
|
}
|
||||||
|
|
||||||
// HACK: https://bugreports.qt.io/browse/QTBUG-83972, qtwayland cannot auto hide menu
|
// HACK: https://bugreports.qt.io/browse/QTBUG-83972, qtwayland cannot auto hide menu
|
||||||
Connections {
|
Connections {
|
||||||
function onHideMenu() {
|
function onHideMenu() {
|
||||||
communityContextMenu.close()
|
communityContextMenu.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
target: MainWindow
|
target: MainWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: communitiesList
|
id: communitiesList
|
||||||
|
|
||||||
|
@ -36,21 +41,161 @@ Page {
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
ScrollBar.vertical: ScrollBar {
|
||||||
id: scrollbar
|
id: scrollbar
|
||||||
|
|
||||||
parent: !collapsed && Settings.scrollbarsInRoomlist ? communitiesList : null
|
parent: !collapsed && Settings.scrollbarsInRoomlist ? communitiesList : null
|
||||||
}
|
}
|
||||||
|
delegate: ItemDelegate {
|
||||||
|
id: communityItem
|
||||||
|
|
||||||
ScrollHelper {
|
property color backgroundColor: palette.window
|
||||||
flickable: parent
|
property color bubbleBackground: palette.highlight
|
||||||
anchors.fill: parent
|
property color bubbleText: palette.highlightedText
|
||||||
enabled: !Settings.mobileMode
|
property color importantText: palette.text
|
||||||
|
required property var model
|
||||||
|
property color unimportantText: palette.buttonText
|
||||||
|
|
||||||
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
ToolTip.text: model.tooltip
|
||||||
|
ToolTip.visible: hovered && collapsed
|
||||||
|
height: avatarSize + 2 * Nheko.paddingMedium
|
||||||
|
state: "normal"
|
||||||
|
width: ListView.view.width - ((scrollbar.interactive && scrollbar.visible && scrollbar.parent) ? scrollbar.width : 0)
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: communityItem.backgroundColor
|
||||||
|
}
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "highlight"
|
||||||
|
when: (communityItem.hovered || model.hidden) && !(Communities.currentTagId === model.id)
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
backgroundColor: palette.dark
|
||||||
|
bubbleBackground: palette.highlight
|
||||||
|
bubbleText: palette.highlightedText
|
||||||
|
importantText: palette.brightText
|
||||||
|
target: communityItem
|
||||||
|
unimportantText: palette.brightText
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "selected"
|
||||||
|
when: Communities.currentTagId == model.id
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
backgroundColor: palette.highlight
|
||||||
|
bubbleBackground: palette.highlightedText
|
||||||
|
bubbleText: palette.highlight
|
||||||
|
importantText: palette.highlightedText
|
||||||
|
target: communityItem
|
||||||
|
unimportantText: palette.highlightedText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
onClicked: Communities.setCurrentTagId(model.id)
|
||||||
|
onPressAndHold: communityContextMenu.show(model.id, model.hidden, model.muted)
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
acceptedButtons: Qt.RightButton
|
||||||
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
||||||
|
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||||
|
|
||||||
|
onSingleTapped: communityContextMenu.show(model.id, model.hidden, model.muted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
id: r
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Nheko.paddingMedium + (communitySidebar.collapsed ? 0 : (fontMetrics.lineSpacing * model.depth))
|
||||||
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
|
ImageButton {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.preferredHeight: fontMetrics.lineSpacing
|
||||||
|
Layout.preferredWidth: fontMetrics.lineSpacing
|
||||||
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
ToolTip.text: model.collapsed ? qsTr("Expand") : qsTr("Collapse")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
height: fontMetrics.lineSpacing
|
||||||
|
hoverEnabled: true
|
||||||
|
image: model.collapsed ? ":/icons/icons/ui/collapsed.svg" : ":/icons/icons/ui/expanded.svg"
|
||||||
|
visible: !communitySidebar.collapsed && model.collapsible
|
||||||
|
width: fontMetrics.lineSpacing
|
||||||
|
|
||||||
|
onClicked: model.collapsed = !model.collapsed
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.preferredWidth: fontMetrics.lineSpacing
|
||||||
|
visible: !communitySidebar.collapsed && !model.collapsible && Communities.containsSubspaces
|
||||||
|
}
|
||||||
|
Avatar {
|
||||||
|
id: avatar
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
color: communityItem.backgroundColor
|
||||||
|
displayName: model.displayName
|
||||||
|
enabled: false
|
||||||
|
height: avatarSize
|
||||||
|
roomid: model.id
|
||||||
|
textColor: model.avatarUrl.startsWith(":/") ? communityItem.unimportantText : communityItem.importantText
|
||||||
|
url: {
|
||||||
|
if (model.avatarUrl.startsWith("mxc://"))
|
||||||
|
return model.avatarUrl.replace("mxc://", "image://MxcImage/");
|
||||||
|
else if (model.avatarUrl.length > 0)
|
||||||
|
return model.avatarUrl;
|
||||||
|
else
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
width: avatarSize
|
||||||
|
|
||||||
|
NotificationBubble {
|
||||||
|
anchors.bottom: avatar.bottom
|
||||||
|
anchors.margins: -Nheko.paddingSmall
|
||||||
|
anchors.right: avatar.right
|
||||||
|
bubbleBackgroundColor: communityItem.bubbleBackground
|
||||||
|
bubbleTextColor: communityItem.bubbleText
|
||||||
|
font.pixelSize: fontMetrics.font.pixelSize * 0.6
|
||||||
|
hasLoudNotification: model.hasLoudNotification
|
||||||
|
mayBeVisible: communitySidebar.collapsed && !model.muted && Settings.spaceNotifications
|
||||||
|
notificationCount: model.unreadMessages
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ElidedLabel {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: communityItem.importantText
|
||||||
|
elideWidth: width
|
||||||
|
fullText: model.displayName
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
visible: !communitySidebar.collapsed
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
NotificationBubble {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
Layout.leftMargin: Nheko.paddingSmall
|
||||||
|
bubbleBackgroundColor: communityItem.bubbleBackground
|
||||||
|
bubbleTextColor: communityItem.bubbleText
|
||||||
|
hasLoudNotification: model.hasLoudNotification
|
||||||
|
mayBeVisible: !communitySidebar.collapsed && !model.muted && Settings.spaceNotifications
|
||||||
|
notificationCount: model.unreadMessages
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.Menu {
|
Platform.Menu {
|
||||||
id: communityContextMenu
|
id: communityContextMenu
|
||||||
|
|
||||||
property string tagId
|
|
||||||
property bool hidden
|
property bool hidden
|
||||||
property bool muted
|
property bool muted
|
||||||
|
property string tagId
|
||||||
|
|
||||||
function show(id_, hidden_, muted_) {
|
function show(id_, hidden_, muted_) {
|
||||||
tagId = id_;
|
tagId = id_;
|
||||||
|
@ -60,177 +205,19 @@ Page {
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Do not show notification counts for this community or tag.")
|
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: communityContextMenu.muted
|
checked: communityContextMenu.muted
|
||||||
|
text: qsTr("Do not show notification counts for this community or tag.")
|
||||||
|
|
||||||
onTriggered: Communities.toggleTagMute(communityContextMenu.tagId)
|
onTriggered: Communities.toggleTagMute(communityContextMenu.tagId)
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Hide rooms with this tag or from this community by default.")
|
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: communityContextMenu.hidden
|
checked: communityContextMenu.hidden
|
||||||
|
text: qsTr("Hide rooms with this tag or from this community by default.")
|
||||||
|
|
||||||
onTriggered: Communities.toggleTagId(communityContextMenu.tagId)
|
onTriggered: Communities.toggleTagId(communityContextMenu.tagId)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: ItemDelegate {
|
|
||||||
id: communityItem
|
|
||||||
|
|
||||||
property color backgroundColor: Nheko.colors.window
|
|
||||||
property color importantText: Nheko.colors.text
|
|
||||||
property color unimportantText: Nheko.colors.buttonText
|
|
||||||
property color bubbleBackground: Nheko.colors.highlight
|
|
||||||
property color bubbleText: Nheko.colors.highlightedText
|
|
||||||
required property var model
|
|
||||||
|
|
||||||
height: avatarSize + 2 * Nheko.paddingMedium
|
|
||||||
width: ListView.view.width - ((scrollbar.interactive && scrollbar.visible && scrollbar.parent) ? scrollbar.width : 0)
|
|
||||||
state: "normal"
|
|
||||||
ToolTip.visible: hovered && collapsed
|
|
||||||
ToolTip.text: model.tooltip
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
|
||||||
onClicked: Communities.setCurrentTagId(model.id)
|
|
||||||
onPressAndHold: communityContextMenu.show(model.id, model.hidden, model.muted)
|
|
||||||
states: [
|
|
||||||
State {
|
|
||||||
name: "highlight"
|
|
||||||
when: (communityItem.hovered || model.hidden) && !(Communities.currentTagId === model.id)
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
target: communityItem
|
|
||||||
backgroundColor: Nheko.colors.dark
|
|
||||||
importantText: Nheko.colors.brightText
|
|
||||||
unimportantText: Nheko.colors.brightText
|
|
||||||
bubbleBackground: Nheko.colors.highlight
|
|
||||||
bubbleText: Nheko.colors.highlightedText
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "selected"
|
|
||||||
when: Communities.currentTagId == model.id
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
target: communityItem
|
|
||||||
backgroundColor: Nheko.colors.highlight
|
|
||||||
importantText: Nheko.colors.highlightedText
|
|
||||||
unimportantText: Nheko.colors.highlightedText
|
|
||||||
bubbleBackground: Nheko.colors.highlightedText
|
|
||||||
bubbleText: Nheko.colors.highlight
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
Item {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
TapHandler {
|
|
||||||
acceptedButtons: Qt.RightButton
|
|
||||||
onSingleTapped: communityContextMenu.show(model.id, model.hidden, model.muted)
|
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
|
||||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: r
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Nheko.paddingMedium
|
|
||||||
anchors.leftMargin: Nheko.paddingMedium + (communitySidebar.collapsed ? 0 : (fontMetrics.lineSpacing * model.depth))
|
|
||||||
|
|
||||||
ImageButton {
|
|
||||||
visible: !communitySidebar.collapsed && model.collapsible
|
|
||||||
Layout.preferredHeight: fontMetrics.lineSpacing
|
|
||||||
Layout.preferredWidth: fontMetrics.lineSpacing
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
height: fontMetrics.lineSpacing
|
|
||||||
width: fontMetrics.lineSpacing
|
|
||||||
image: model.collapsed ? ":/icons/icons/ui/collapsed.svg" : ":/icons/icons/ui/expanded.svg"
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
|
||||||
ToolTip.text: model.collapsed ? qsTr("Expand") : qsTr("Collapse")
|
|
||||||
hoverEnabled: true
|
|
||||||
|
|
||||||
onClicked: model.collapsed = !model.collapsed
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.preferredWidth: fontMetrics.lineSpacing
|
|
||||||
visible: !communitySidebar.collapsed && !model.collapsible && Communities.containsSubspaces
|
|
||||||
}
|
|
||||||
|
|
||||||
Avatar {
|
|
||||||
id: avatar
|
|
||||||
|
|
||||||
enabled: false
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
height: avatarSize
|
|
||||||
width: avatarSize
|
|
||||||
url: {
|
|
||||||
if (model.avatarUrl.startsWith("mxc://"))
|
|
||||||
return model.avatarUrl.replace("mxc://", "image://MxcImage/");
|
|
||||||
else
|
|
||||||
return "image://colorimage/" + model.avatarUrl + "?" + communityItem.unimportantText;
|
|
||||||
}
|
|
||||||
roomid: model.id
|
|
||||||
displayName: model.displayName
|
|
||||||
color: communityItem.backgroundColor
|
|
||||||
|
|
||||||
NotificationBubble {
|
|
||||||
notificationCount: model.unreadMessages
|
|
||||||
hasLoudNotification: model.hasLoudNotification
|
|
||||||
bubbleBackgroundColor: communityItem.bubbleBackground
|
|
||||||
bubbleTextColor: communityItem.bubbleText
|
|
||||||
font.pixelSize: fontMetrics.font.pixelSize * 0.6
|
|
||||||
mayBeVisible: communitySidebar.collapsed && !model.muted && Settings.spaceNotifications
|
|
||||||
anchors.right: avatar.right
|
|
||||||
anchors.bottom: avatar.bottom
|
|
||||||
anchors.margins: -Nheko.paddingSmall
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ElidedLabel {
|
|
||||||
visible: !communitySidebar.collapsed
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
color: communityItem.importantText
|
|
||||||
Layout.fillWidth: true
|
|
||||||
elideWidth: width
|
|
||||||
fullText: model.displayName
|
|
||||||
textFormat: Text.PlainText
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
NotificationBubble {
|
|
||||||
notificationCount: model.unreadMessages
|
|
||||||
hasLoudNotification: model.hasLoudNotification
|
|
||||||
bubbleBackgroundColor: communityItem.bubbleBackground
|
|
||||||
bubbleTextColor: communityItem.bubbleText
|
|
||||||
mayBeVisible: !communitySidebar.collapsed && !model.muted && Settings.spaceNotifications
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
Layout.leftMargin: Nheko.paddingSmall
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: communityItem.backgroundColor
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: Nheko.theme.sidebarBackground
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,124 +11,107 @@ import im.nheko 1.0
|
||||||
Control {
|
Control {
|
||||||
id: popup
|
id: popup
|
||||||
|
|
||||||
property alias currentIndex: listView.currentIndex
|
|
||||||
property string roomId
|
|
||||||
property string completerName
|
|
||||||
property var completer
|
|
||||||
property bool bottomToTop: true
|
|
||||||
property bool fullWidth: false
|
|
||||||
property bool centerRowContent: true
|
|
||||||
property int avatarHeight: 24
|
property int avatarHeight: 24
|
||||||
property int avatarWidth: 24
|
property int avatarWidth: 24
|
||||||
|
property bool bottomToTop: true
|
||||||
|
property bool centerRowContent: true
|
||||||
|
property var completer
|
||||||
|
property string completerName
|
||||||
|
property alias count: listView.count
|
||||||
|
property alias currentIndex: listView.currentIndex
|
||||||
|
property bool fullWidth: false
|
||||||
|
property string roomId
|
||||||
property int rowMargin: 0
|
property int rowMargin: 0
|
||||||
property int rowSpacing: Nheko.paddingSmall
|
property int rowSpacing: Nheko.paddingSmall
|
||||||
property alias count: listView.count
|
|
||||||
|
|
||||||
signal completionClicked(string completion)
|
signal completionClicked(string completion)
|
||||||
signal completionSelected(string id)
|
signal completionSelected(string id)
|
||||||
|
|
||||||
function up() {
|
function changeCompleter() {
|
||||||
if (bottomToTop)
|
if (completerName) {
|
||||||
down_();
|
completer = TimelineManager.completerFor(completerName, completerName == "room" ? "" : (popup.roomId != "" ? popup.roomId : room.roomId));
|
||||||
else
|
completer.setSearchString("");
|
||||||
up_();
|
} else {
|
||||||
|
completer = undefined;
|
||||||
|
}
|
||||||
|
currentIndex = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function down() {
|
|
||||||
if (bottomToTop)
|
|
||||||
up_();
|
|
||||||
else
|
|
||||||
down_();
|
|
||||||
}
|
|
||||||
|
|
||||||
function up_() {
|
|
||||||
currentIndex = currentIndex - 1;
|
|
||||||
if (currentIndex == -2)
|
|
||||||
currentIndex = listView.count - 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function down_() {
|
|
||||||
currentIndex = currentIndex + 1;
|
|
||||||
if (currentIndex >= listView.count)
|
|
||||||
currentIndex = -1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function currentCompletion() {
|
function currentCompletion() {
|
||||||
if (currentIndex > -1 && currentIndex < listView.count)
|
if (currentIndex > -1 && currentIndex < listView.count)
|
||||||
return completer.completionAt(currentIndex);
|
return completer.completionAt(currentIndex);
|
||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
function down() {
|
||||||
|
if (bottomToTop)
|
||||||
|
up_();
|
||||||
|
else
|
||||||
|
down_();
|
||||||
|
}
|
||||||
|
function down_() {
|
||||||
|
currentIndex = currentIndex + 1;
|
||||||
|
if (currentIndex >= listView.count)
|
||||||
|
currentIndex = -1;
|
||||||
|
}
|
||||||
function finishCompletion() {
|
function finishCompletion() {
|
||||||
if (popup.completerName == "room")
|
if (popup.completerName == "room")
|
||||||
popup.completionSelected(listView.itemAtIndex(currentIndex).modelData.roomid);
|
popup.completionSelected(listView.itemAtIndex(currentIndex).modelData.roomid);
|
||||||
else if (popup.completerName == "user")
|
else if (popup.completerName == "user")
|
||||||
popup.completionSelected(listView.itemAtIndex(currentIndex).modelData.userid);
|
popup.completionSelected(listView.itemAtIndex(currentIndex).modelData.userid);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
function up() {
|
||||||
function changeCompleter() {
|
if (bottomToTop)
|
||||||
if (completerName) {
|
down_();
|
||||||
completer = TimelineManager.completerFor(completerName, completerName == "room" ? "" : (popup.roomId != "" ? popup.roomId : room.roomId));
|
else
|
||||||
completer.setSearchString("");
|
up_();
|
||||||
} else {
|
}
|
||||||
completer = undefined;
|
function up_() {
|
||||||
}
|
currentIndex = currentIndex - 1;
|
||||||
currentIndex = -1
|
if (currentIndex == -2)
|
||||||
|
currentIndex = listView.count - 1;
|
||||||
}
|
}
|
||||||
onCompleterNameChanged: changeCompleter()
|
|
||||||
onRoomIdChanged: changeCompleter()
|
|
||||||
|
|
||||||
bottomPadding: 1
|
bottomPadding: 1
|
||||||
leftPadding: 1
|
leftPadding: 1
|
||||||
topPadding: 1
|
|
||||||
rightPadding: 1
|
|
||||||
|
|
||||||
|
// Workaround palettes not inheriting for popups
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
rightPadding: 1
|
||||||
|
topPadding: 1
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: palette.mid
|
||||||
|
color: palette.base
|
||||||
|
}
|
||||||
contentItem: ListView {
|
contentItem: ListView {
|
||||||
id: listView
|
id: listView
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
displayMarginBeginning: height / 2
|
||||||
|
displayMarginEnd: height / 2
|
||||||
|
highlightFollowsCurrentItem: true
|
||||||
|
|
||||||
// If we have fewer than 7 items, just use the list view's content height.
|
// If we have fewer than 7 items, just use the list view's content height.
|
||||||
// Otherwise, we want to show 7 items. Each item consists of row spacing between rows, row margins
|
// Otherwise, we want to show 7 items. Each item consists of row spacing between rows, row margins
|
||||||
// on each side of a row, 1px of padding above the first item and below the last item, and nominally
|
// on each side of a row, 1px of padding above the first item and below the last item, and nominally
|
||||||
// some kind of content height. avatarHeight is used for just about every delegate, so we're using
|
// some kind of content height. avatarHeight is used for just about every delegate, so we're using
|
||||||
// that until we find something better. Put is all together and you have the formula below!
|
// that until we find something better. Put is all together and you have the formula below!
|
||||||
implicitHeight: Math.min(contentHeight, 6*rowSpacing + 7*(popup.avatarHeight + 2*rowMargin))
|
implicitHeight: Math.min(contentHeight, 6 * rowSpacing + 7 * (popup.avatarHeight + 2 * rowMargin))
|
||||||
clip: true
|
|
||||||
ScrollHelper {
|
|
||||||
flickable: parent
|
|
||||||
anchors.fill: parent
|
|
||||||
enabled: !Settings.mobileMode
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: deadTimer
|
|
||||||
interval: 50
|
|
||||||
}
|
|
||||||
|
|
||||||
onContentYChanged: deadTimer.restart()
|
|
||||||
|
|
||||||
// Broken, see https://bugreports.qt.io/browse/QTBUG-102811
|
// Broken, see https://bugreports.qt.io/browse/QTBUG-102811
|
||||||
//reuseItems: true
|
//reuseItems: true
|
||||||
implicitWidth: listView.contentItem.childrenRect.width
|
implicitWidth: Math.max(listView.contentItem.childrenRect.width, 20)
|
||||||
model: completer
|
model: completer
|
||||||
verticalLayoutDirection: popup.bottomToTop ? ListView.BottomToTop : ListView.TopToBottom
|
|
||||||
spacing: rowSpacing
|
|
||||||
pixelAligned: true
|
pixelAligned: true
|
||||||
highlightFollowsCurrentItem: true
|
spacing: rowSpacing
|
||||||
|
verticalLayoutDirection: popup.bottomToTop ? ListView.BottomToTop : ListView.TopToBottom
|
||||||
displayMarginBeginning: height / 2
|
|
||||||
displayMarginEnd: height / 2
|
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
property variant modelData: model
|
property variant modelData: model
|
||||||
|
|
||||||
ListView.delayRemove: true
|
ListView.delayRemove: true
|
||||||
|
color: model.index == popup.currentIndex ? palette.highlight : palette.base
|
||||||
color: model.index == popup.currentIndex ? Nheko.colors.highlight : Nheko.colors.base
|
height: (chooser.child?.implicitHeight ?? 0) + 2 * popup.rowMargin
|
||||||
height: chooser.child.implicitHeight + 2 * popup.rowMargin
|
|
||||||
implicitWidth: fullWidth ? ListView.view.width : chooser.child.implicitWidth + 4
|
implicitWidth: fullWidth ? ListView.view.width : chooser.child.implicitWidth + 4
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
@ -136,26 +119,27 @@ Control {
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
onPositionChanged: if (!listView.moving && !deadTimer.running) popup.currentIndex = model.index
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
popup.completionClicked(completer.completionAt(model.index));
|
popup.completionClicked(completer.completionAt(model.index));
|
||||||
if (popup.completerName == "room")
|
if (popup.completerName == "room")
|
||||||
popup.completionSelected(model.roomid);
|
popup.completionSelected(model.roomid);
|
||||||
else if (popup.completerName == "user")
|
else if (popup.completerName == "user")
|
||||||
popup.completionSelected(model.userid);
|
popup.completionSelected(model.userid);
|
||||||
}
|
}
|
||||||
|
onPositionChanged: if (!listView.moving && !deadTimer.running)
|
||||||
|
popup.currentIndex = model.index
|
||||||
}
|
}
|
||||||
Ripple {
|
Ripple {
|
||||||
color: Qt.rgba(Nheko.colors.base.r, Nheko.colors.base.g, Nheko.colors.base.b, 0.5)
|
color: Qt.rgba(palette.base.r, palette.base.g, palette.base.b, 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChooser {
|
DelegateChooser {
|
||||||
id: chooser
|
id: chooser
|
||||||
|
|
||||||
roleValue: popup.completerName
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: popup.rowMargin
|
anchors.margins: popup.rowMargin
|
||||||
enabled: false
|
enabled: false
|
||||||
|
roleValue: popup.completerName
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: "user"
|
roleValue: "user"
|
||||||
|
@ -167,28 +151,23 @@ Control {
|
||||||
spacing: rowSpacing
|
spacing: rowSpacing
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
height: popup.avatarHeight
|
|
||||||
width: popup.avatarWidth
|
|
||||||
displayName: model.displayName
|
displayName: model.displayName
|
||||||
userid: model.userid
|
|
||||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
enabled: false
|
enabled: false
|
||||||
|
height: popup.avatarHeight
|
||||||
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
userid: model.userid
|
||||||
|
width: popup.avatarWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
|
||||||
text: model.displayName
|
text: model.displayName
|
||||||
color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.buttonText
|
||||||
text: "(" + model.userid + ")"
|
text: "(" + model.userid + ")"
|
||||||
color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.buttonText
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: "emoji"
|
roleValue: "emoji"
|
||||||
|
|
||||||
|
@ -199,39 +178,33 @@ Control {
|
||||||
spacing: rowSpacing
|
spacing: rowSpacing
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
visible: !!model.unicode
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
|
||||||
text: model.unicode
|
|
||||||
color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.text
|
|
||||||
font: Settings.emojiFont
|
font: Settings.emojiFont
|
||||||
|
text: model.unicode
|
||||||
|
visible: !!model.unicode
|
||||||
}
|
}
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
visible: !model.unicode
|
crop: false
|
||||||
height: popup.avatarHeight
|
|
||||||
width: popup.avatarWidth
|
|
||||||
displayName: model.shortcode
|
displayName: model.shortcode
|
||||||
|
enabled: false
|
||||||
|
height: popup.avatarHeight
|
||||||
//userid: model.shortcode
|
//userid: model.shortcode
|
||||||
url: (model.url ? model.url : "").replace("mxc://", "image://MxcImage/")
|
url: (model.url ? model.url : "").replace("mxc://", "image://MxcImage/")
|
||||||
enabled: false
|
visible: !model.unicode
|
||||||
crop: false
|
width: popup.avatarWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.leftMargin: Nheko.paddingSmall
|
Layout.leftMargin: Nheko.paddingSmall
|
||||||
Layout.rightMargin: Nheko.paddingSmall
|
Layout.rightMargin: Nheko.paddingSmall
|
||||||
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
|
||||||
text: model.shortcode
|
text: model.shortcode
|
||||||
color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.buttonText
|
||||||
text: "(" + model.packname + ")"
|
text: "(" + model.packname + ")"
|
||||||
color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.buttonText
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: "command"
|
roleValue: "command"
|
||||||
|
|
||||||
|
@ -242,20 +215,16 @@ Control {
|
||||||
spacing: rowSpacing
|
spacing: rowSpacing
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: model.name
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
|
||||||
color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.text
|
|
||||||
font.bold: true
|
font.bold: true
|
||||||
|
text: model.name
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.buttonText
|
||||||
text: model.description
|
text: model.description
|
||||||
color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.buttonText
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: "room"
|
roleValue: "room"
|
||||||
|
|
||||||
|
@ -266,26 +235,22 @@ Control {
|
||||||
spacing: rowSpacing
|
spacing: rowSpacing
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
height: popup.avatarHeight
|
|
||||||
width: popup.avatarWidth
|
|
||||||
displayName: model.roomName
|
displayName: model.roomName
|
||||||
|
enabled: false
|
||||||
|
height: popup.avatarHeight
|
||||||
roomid: model.roomid
|
roomid: model.roomid
|
||||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
enabled: false
|
width: popup.avatarWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: model.roomName
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
|
||||||
font.pixelSize: popup.avatarHeight * 0.5
|
|
||||||
color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.text
|
|
||||||
font.italic: model.isTombstoned
|
font.italic: model.isTombstoned
|
||||||
|
font.pixelSize: popup.avatarHeight * 0.5
|
||||||
|
text: model.roomName
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: "roomAliases"
|
roleValue: "roomAliases"
|
||||||
|
|
||||||
|
@ -296,41 +261,38 @@ Control {
|
||||||
spacing: rowSpacing
|
spacing: rowSpacing
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
height: popup.avatarHeight
|
|
||||||
width: popup.avatarWidth
|
|
||||||
displayName: model.roomName
|
displayName: model.roomName
|
||||||
|
enabled: false
|
||||||
|
height: popup.avatarHeight
|
||||||
roomid: model.roomid
|
roomid: model.roomid
|
||||||
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
enabled: false
|
width: popup.avatarWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: model.roomName
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
|
||||||
color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.text
|
|
||||||
font.italic: model.isTombstoned
|
font.italic: model.isTombstoned
|
||||||
|
text: model.roomName
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
color: model.index == popup.currentIndex ? palette.highlightedText : palette.buttonText
|
||||||
text: "(" + model.roomAlias + ")"
|
text: "(" + model.roomAlias + ")"
|
||||||
color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.buttonText
|
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onContentYChanged: deadTimer.restart()
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: deadTimer
|
||||||
|
|
||||||
|
interval: 50
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onCompleterNameChanged: changeCompleter()
|
||||||
background: Rectangle {
|
onRoomIdChanged: changeCompleter()
|
||||||
color: Nheko.colors.base
|
|
||||||
border.color: Nheko.colors.mid
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,21 +9,20 @@ import im.nheko 1.0
|
||||||
Label {
|
Label {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property alias fullText: metrics.text
|
|
||||||
property alias elideWidth: metrics.elideWidth
|
property alias elideWidth: metrics.elideWidth
|
||||||
|
property alias fullText: metrics.text
|
||||||
property int fullTextWidth: Math.ceil(metrics.advanceWidth)
|
property int fullTextWidth: Math.ceil(metrics.advanceWidth)
|
||||||
|
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
text: (textFormat == Text.PlainText) ? metrics.elidedText : TimelineManager.escapeEmoji(metrics.elidedText)
|
|
||||||
maximumLineCount: 1
|
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
|
maximumLineCount: 1
|
||||||
|
text: (textFormat == Text.PlainText) ? metrics.elidedText : TimelineManager.escapeEmoji(metrics.elidedText)
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
|
|
||||||
TextMetrics {
|
TextMetrics {
|
||||||
id: metrics
|
id: metrics
|
||||||
|
|
||||||
font.pointSize: root.font.pointSize
|
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
|
font.pointSize: root.font.pointSize
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,51 +11,29 @@ Image {
|
||||||
id: stateImg
|
id: stateImg
|
||||||
|
|
||||||
property bool encrypted: false
|
property bool encrypted: false
|
||||||
property int trust: Crypto.Unverified
|
|
||||||
property string unencryptedIcon: ":/icons/icons/ui/shield-filled-cross.svg"
|
|
||||||
property color unencryptedColor: Nheko.theme.error
|
|
||||||
property color unencryptedHoverColor: unencryptedColor
|
|
||||||
property bool hovered: ma.hovered
|
property bool hovered: ma.hovered
|
||||||
|
|
||||||
property string sourceUrl: {
|
property string sourceUrl: {
|
||||||
if (!encrypted)
|
if (!encrypted)
|
||||||
return "image://colorimage/" + unencryptedIcon + "?";
|
return "image://colorimage/" + unencryptedIcon + "?";
|
||||||
|
|
||||||
switch (trust) {
|
switch (trust) {
|
||||||
case Crypto.Verified:
|
case Crypto.Verified:
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled-checkmark.svg?";
|
return "image://colorimage/:/icons/icons/ui/shield-filled-checkmark.svg?";
|
||||||
case Crypto.TOFU:
|
case Crypto.TOFU:
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled.svg?";
|
return "image://colorimage/:/icons/icons/ui/shield-filled.svg?";
|
||||||
case Crypto.Unverified:
|
case Crypto.Unverified:
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled-exclamation-mark.svg?";
|
return "image://colorimage/:/icons/icons/ui/shield-filled-exclamation-mark.svg?";
|
||||||
default:
|
default:
|
||||||
return "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?";
|
return "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
property int trust: Crypto.Unverified
|
||||||
|
property color unencryptedColor: Nheko.theme.error
|
||||||
|
property color unencryptedHoverColor: unencryptedColor
|
||||||
|
property string unencryptedIcon: ":/icons/icons/ui/shield-filled-cross.svg"
|
||||||
|
|
||||||
width: 16
|
|
||||||
height: 16
|
|
||||||
sourceSize.height: height
|
|
||||||
sourceSize.width: width
|
|
||||||
source: {
|
|
||||||
if (encrypted) {
|
|
||||||
switch (trust) {
|
|
||||||
case Crypto.Verified:
|
|
||||||
return sourceUrl + Nheko.theme.green;
|
|
||||||
case Crypto.TOFU:
|
|
||||||
return sourceUrl + Nheko.colors.buttonText;
|
|
||||||
default:
|
|
||||||
return sourceUrl + Nheko.theme.error;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return sourceUrl + (stateImg.hovered ? unencryptedHoverColor : unencryptedColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ToolTip.visible: stateImg.hovered
|
|
||||||
ToolTip.text: {
|
ToolTip.text: {
|
||||||
if (!encrypted)
|
if (!encrypted)
|
||||||
return qsTr("This message is not encrypted!");
|
return qsTr("This message is not encrypted!");
|
||||||
|
|
||||||
switch (trust) {
|
switch (trust) {
|
||||||
case Crypto.Verified:
|
case Crypto.Verified:
|
||||||
return qsTr("Encrypted by a verified device");
|
return qsTr("Encrypted by a verified device");
|
||||||
|
@ -65,9 +43,28 @@ Image {
|
||||||
return qsTr("Encrypted by an unverified device or the key is from an untrusted source like the key backup.");
|
return qsTr("Encrypted by an unverified device or the key is from an untrusted source like the key backup.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ToolTip.visible: stateImg.hovered
|
||||||
|
height: 16
|
||||||
|
source: {
|
||||||
|
if (encrypted) {
|
||||||
|
switch (trust) {
|
||||||
|
case Crypto.Verified:
|
||||||
|
return sourceUrl + Nheko.theme.green;
|
||||||
|
case Crypto.TOFU:
|
||||||
|
return sourceUrl + palette.buttonText;
|
||||||
|
default:
|
||||||
|
return sourceUrl + Nheko.theme.error;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return sourceUrl + (stateImg.hovered ? unencryptedHoverColor : unencryptedColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sourceSize.height: height
|
||||||
|
sourceSize.width: width
|
||||||
|
width: 16
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: ma
|
id: ma
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,14 +16,24 @@ Popup {
|
||||||
mid = mid_in;
|
mid = mid_in;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
leftPadding: 10
|
||||||
|
modal: true
|
||||||
|
|
||||||
|
// Workaround palettes not inheriting for popups
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
parent: Overlay.overlay
|
||||||
|
rightPadding: 10
|
||||||
|
width: timelineRoot.width * 0.8
|
||||||
x: Math.round(parent.width / 2 - width / 2)
|
x: Math.round(parent.width / 2 - width / 2)
|
||||||
y: Math.round(parent.height / 4)
|
y: Math.round(parent.height / 4)
|
||||||
modal: true
|
|
||||||
palette: Nheko.colors
|
Overlay.modal: Rectangle {
|
||||||
parent: Overlay.overlay
|
color: Qt.rgba(palette.window.r, palette.window.g, palette.window.b, 0.7)
|
||||||
width: timelineRoot.width * 0.8
|
}
|
||||||
leftPadding: 10
|
background: Rectangle {
|
||||||
rightPadding: 10
|
color: palette.window
|
||||||
|
}
|
||||||
|
|
||||||
onOpened: {
|
onOpened: {
|
||||||
roomTextInput.forceActiveFocus();
|
roomTextInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
|
@ -36,46 +46,40 @@ Popup {
|
||||||
Label {
|
Label {
|
||||||
id: titleLabel
|
id: titleLabel
|
||||||
|
|
||||||
text: qsTr("Forward Message")
|
|
||||||
font.bold: true
|
|
||||||
bottomPadding: 10
|
bottomPadding: 10
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
|
font.bold: true
|
||||||
|
text: qsTr("Forward Message")
|
||||||
}
|
}
|
||||||
|
|
||||||
Reply {
|
Reply {
|
||||||
id: replyPreview
|
id: replyPreview
|
||||||
|
|
||||||
property var modelData: room ? room.getDump(mid, "") : {
|
property var modelData: room ? room.getDump(mid, "") : {}
|
||||||
}
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
userColor: TimelineManager.userColor(modelData.userId, Nheko.colors.window)
|
|
||||||
blurhash: modelData.blurhash ?? ""
|
blurhash: modelData.blurhash ?? ""
|
||||||
body: modelData.body ?? ""
|
body: modelData.body ?? ""
|
||||||
formattedBody: modelData.formattedBody ?? ""
|
encryptionError: modelData.encryptionError ?? ""
|
||||||
eventId: modelData.eventId ?? ""
|
eventId: modelData.eventId ?? ""
|
||||||
filename: modelData.filename ?? ""
|
filename: modelData.filename ?? ""
|
||||||
filesize: modelData.filesize ?? ""
|
filesize: modelData.filesize ?? ""
|
||||||
|
formattedBody: modelData.formattedBody ?? ""
|
||||||
|
isOnlyEmoji: modelData.isOnlyEmoji ?? false
|
||||||
|
originalWidth: modelData.originalWidth ?? 0
|
||||||
proportionalHeight: modelData.proportionalHeight ?? 1
|
proportionalHeight: modelData.proportionalHeight ?? 1
|
||||||
type: modelData.type ?? MtxEvent.UnknownMessage
|
type: modelData.type ?? MtxEvent.UnknownMessage
|
||||||
typeString: modelData.typeString ?? ""
|
typeString: modelData.typeString ?? ""
|
||||||
url: modelData.url ?? ""
|
url: modelData.url ?? ""
|
||||||
originalWidth: modelData.originalWidth ?? 0
|
userColor: TimelineManager.userColor(modelData.userId, palette.window)
|
||||||
isOnlyEmoji: modelData.isOnlyEmoji ?? false
|
|
||||||
userId: modelData.userId ?? ""
|
userId: modelData.userId ?? ""
|
||||||
userName: modelData.userName ?? ""
|
userName: modelData.userName ?? ""
|
||||||
encryptionError: modelData.encryptionError ?? ""
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: roomTextInput
|
id: roomTextInput
|
||||||
|
|
||||||
|
color: palette.text
|
||||||
width: forwardMessagePopup.width - forwardMessagePopup.leftPadding * 2
|
width: forwardMessagePopup.width - forwardMessagePopup.leftPadding * 2
|
||||||
color: Nheko.colors.text
|
|
||||||
onTextEdited: {
|
|
||||||
completerPopup.completer.searchString = text;
|
|
||||||
}
|
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
if (event.key == Qt.Key_Up || event.key == Qt.Key_Backtab) {
|
if (event.key == Qt.Key_Up || event.key == Qt.Key_Backtab) {
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
|
@ -91,43 +95,32 @@ Popup {
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onTextEdited: {
|
||||||
|
completerPopup.completer.searchString = text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Completer {
|
Completer {
|
||||||
id: completerPopup
|
id: completerPopup
|
||||||
|
|
||||||
width: forwardMessagePopup.width - forwardMessagePopup.leftPadding * 2
|
|
||||||
completerName: "room"
|
|
||||||
fullWidth: true
|
|
||||||
centerRowContent: false
|
|
||||||
avatarHeight: 24
|
avatarHeight: 24
|
||||||
avatarWidth: 24
|
avatarWidth: 24
|
||||||
bottomToTop: false
|
bottomToTop: false
|
||||||
|
centerRowContent: false
|
||||||
|
completerName: "room"
|
||||||
|
fullWidth: true
|
||||||
|
width: forwardMessagePopup.width - forwardMessagePopup.leftPadding * 2
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onCompletionSelected(id) {
|
function onCompletionSelected(id) {
|
||||||
room.forwardMessage(messageContextMenu.eventId, id);
|
room.forwardMessage(messageContextMenu.eventId, id);
|
||||||
forwardMessagePopup.close();
|
forwardMessagePopup.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCountChanged() {
|
function onCountChanged() {
|
||||||
if (completerPopup.count > 0 && (completerPopup.currentIndex < 0 || completerPopup.currentIndex >= completerPopup.count))
|
if (completerPopup.count > 0 && (completerPopup.currentIndex < 0 || completerPopup.currentIndex >= completerPopup.count))
|
||||||
completerPopup.currentIndex = 0;
|
completerPopup.currentIndex = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target: completerPopup
|
target: completerPopup
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: Nheko.colors.window
|
|
||||||
}
|
|
||||||
|
|
||||||
Overlay.modal: Rectangle {
|
|
||||||
color: Qt.rgba(Nheko.colors.window.r, Nheko.colors.window.g, Nheko.colors.window.b, 0.7)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
import "./ui"
|
import "./ui"
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
|
@ -10,38 +11,35 @@ import im.nheko 1.0 // for cursor shape
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: button
|
id: button
|
||||||
|
|
||||||
property alias cursor: mouseArea.cursorShape
|
property color buttonTextColor: palette.buttonText
|
||||||
property string image: undefined
|
|
||||||
property color highlightColor: Nheko.colors.highlight
|
|
||||||
property color buttonTextColor: Nheko.colors.buttonText
|
|
||||||
property bool changeColorOnHover: true
|
property bool changeColorOnHover: true
|
||||||
|
property alias cursor: mouseArea.cursorShape
|
||||||
|
property color highlightColor: palette.highlight
|
||||||
|
property string image: undefined
|
||||||
property bool ripple: true
|
property bool ripple: true
|
||||||
|
|
||||||
focusPolicy: Qt.NoFocus
|
focusPolicy: Qt.NoFocus
|
||||||
width: 16
|
|
||||||
height: 16
|
height: 16
|
||||||
|
width: 16
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: buttonImg
|
id: buttonImg
|
||||||
|
|
||||||
// Workaround, can't get icon.source working for now...
|
// Workaround, can't get icon.source working for now...
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: image != "" ? ("image://colorimage/" + image + "?" + ((button.hovered && changeColorOnHover) ? highlightColor : buttonTextColor)) : ""
|
fillMode: Image.PreserveAspectFit
|
||||||
|
source: button.image != "" ? ("image://colorimage/" + button.image + "?" + ((button.hovered && button.changeColorOnHover) ? button.highlightColor : button.buttonTextColor)) : ""
|
||||||
sourceSize.height: button.height
|
sourceSize.height: button.height
|
||||||
sourceSize.width: button.width
|
sourceSize.width: button.width
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
}
|
}
|
||||||
|
NhekoCursorShape {
|
||||||
CursorShape {
|
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
Ripple {
|
Ripple {
|
||||||
|
color: Qt.rgba(button.buttonTextColor.r, button.buttonTextColor.g, button.buttonTextColor.b, 0.5)
|
||||||
enabled: button.ripple
|
enabled: button.ripple
|
||||||
color: Qt.rgba(buttonTextColor.r, buttonTextColor.g, buttonTextColor.b, 0.5)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,28 +11,42 @@ TextEdit {
|
||||||
|
|
||||||
property alias cursorShape: cs.cursorShape
|
property alias cursorShape: cs.cursorShape
|
||||||
|
|
||||||
textFormat: TextEdit.RichText
|
//leftInset: 0
|
||||||
readOnly: true
|
//bottomInset: 0
|
||||||
focus: false
|
//rightInset: 0
|
||||||
wrapMode: Text.Wrap
|
//topInset: 0
|
||||||
selectByMouse: !Settings.mobileMode
|
//leftPadding: 0
|
||||||
|
//bottomPadding: 0
|
||||||
|
//rightPadding: 0
|
||||||
|
//topPadding: 0
|
||||||
|
//background: null
|
||||||
|
|
||||||
|
ToolTip.text: hoveredLink
|
||||||
|
ToolTip.visible: hoveredLink || false
|
||||||
// this always has to be enabled, otherwise you can't click links anymore!
|
// this always has to be enabled, otherwise you can't click links anymore!
|
||||||
//enabled: selectByMouse
|
//enabled: selectByMouse
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
onLinkActivated: Nheko.openLink(link)
|
focus: false
|
||||||
ToolTip.visible: hoveredLink || false
|
readOnly: true
|
||||||
ToolTip.text: hoveredLink
|
selectByMouse: !Settings.mobileMode
|
||||||
|
textFormat: TextEdit.RichText
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
// Setting a tooltip delay makes the hover text empty .-.
|
// Setting a tooltip delay makes the hover text empty .-.
|
||||||
//ToolTip.delay: Nheko.tooltipDelay
|
//ToolTip.delay: Nheko.tooltipDelay
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
TimelineManager.fixImageRendering(r.textDocument, r);
|
TimelineManager.fixImageRendering(r.textDocument, r);
|
||||||
}
|
}
|
||||||
|
onLinkActivated: Nheko.openLink(link)
|
||||||
|
|
||||||
CursorShape {
|
//// propagate events up
|
||||||
|
//onPressAndHold: (event) => event.accepted = false
|
||||||
|
//onPressed: (event) => event.accepted = (event.button == Qt.LeftButton)
|
||||||
|
|
||||||
|
NhekoCursorShape {
|
||||||
id: cs
|
id: cs
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
cursorShape: hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,68 +7,63 @@ import QtQuick.Controls 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: c
|
id: c
|
||||||
property color backgroundColor: Nheko.colors.base
|
|
||||||
|
property color backgroundColor: palette.base
|
||||||
property alias color: labelC.color
|
property alias color: labelC.color
|
||||||
property alias textPadding: input.padding
|
property alias echoMode: input.echoMode
|
||||||
property alias text: input.text
|
property alias font: input.font
|
||||||
|
property var hasClear: false
|
||||||
property alias label: labelC.text
|
property alias label: labelC.text
|
||||||
property alias placeholderText: input.placeholderText
|
property alias placeholderText: input.placeholderText
|
||||||
property alias font: input.font
|
|
||||||
property alias echoMode: input.echoMode
|
|
||||||
property alias selectByMouse: input.selectByMouse
|
property alias selectByMouse: input.selectByMouse
|
||||||
property var hasClear: false
|
property alias text: input.text
|
||||||
|
property alias textPadding: input.padding
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: timer
|
|
||||||
interval: 350
|
|
||||||
onTriggered: editingFinished()
|
|
||||||
}
|
|
||||||
|
|
||||||
onTextChanged: timer.restart()
|
|
||||||
|
|
||||||
signal textEdited
|
|
||||||
signal accepted
|
signal accepted
|
||||||
signal editingFinished
|
signal editingFinished
|
||||||
|
signal textEdited
|
||||||
function forceActiveFocus() {
|
|
||||||
input.forceActiveFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
function clear() {
|
function clear() {
|
||||||
input.clear();
|
input.clear();
|
||||||
}
|
}
|
||||||
|
function forceActiveFocus() {
|
||||||
|
input.forceActiveFocus();
|
||||||
|
}
|
||||||
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.visible: hover.hovered
|
ToolTip.visible: hover.hovered
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Item {
|
onTextChanged: timer.restart()
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: labelC.contentHeight
|
|
||||||
Layout.margins: input.padding
|
|
||||||
Layout.bottomMargin: Nheko.paddingSmall
|
|
||||||
visible: labelC.text
|
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: timer
|
||||||
|
|
||||||
|
interval: 350
|
||||||
|
|
||||||
|
onTriggered: editingFinished()
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.bottomMargin: Nheko.paddingSmall
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: input.padding
|
||||||
|
Layout.preferredHeight: labelC.contentHeight
|
||||||
|
visible: labelC.text
|
||||||
z: 1
|
z: 1
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: labelC
|
id: labelC
|
||||||
|
|
||||||
y: contentHeight + input.padding + Nheko.paddingSmall
|
color: palette.text
|
||||||
enabled: false
|
enabled: false
|
||||||
|
font.letterSpacing: input.font.pixelSize * 0.02
|
||||||
palette: Nheko.colors
|
|
||||||
color: Nheko.colors.text
|
|
||||||
font.pixelSize: input.font.pixelSize
|
font.pixelSize: input.font.pixelSize
|
||||||
font.weight: Font.DemiBold
|
font.weight: Font.DemiBold
|
||||||
font.letterSpacing: input.font.pixelSize * 0.02
|
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
state: labelC.text && (input.activeFocus == true || input.text) ? "focused" : ""
|
state: labelC.text && (input.activeFocus == true || input.text) ? "focused" : ""
|
||||||
|
width: parent.width
|
||||||
|
y: contentHeight + input.padding + Nheko.paddingSmall
|
||||||
|
|
||||||
states: State {
|
states: State {
|
||||||
name: "focused"
|
name: "focused"
|
||||||
|
@ -77,51 +72,40 @@ ColumnLayout {
|
||||||
target: labelC
|
target: labelC
|
||||||
y: 0
|
y: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: input
|
|
||||||
opacity: 1
|
opacity: 1
|
||||||
|
target: input
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
transitions: Transition {
|
transitions: Transition {
|
||||||
from: ""
|
from: ""
|
||||||
to: "focused"
|
|
||||||
reversible: true
|
reversible: true
|
||||||
|
to: "focused"
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: labelC
|
alwaysRunToEnd: true
|
||||||
|
duration: 210
|
||||||
|
easing.type: Easing.InCubic
|
||||||
properties: "y"
|
properties: "y"
|
||||||
duration: 210
|
target: labelC
|
||||||
easing.type: Easing.InCubic
|
|
||||||
alwaysRunToEnd: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: input
|
alwaysRunToEnd: true
|
||||||
properties: "opacity"
|
|
||||||
duration: 210
|
duration: 210
|
||||||
easing.type: Easing.InCubic
|
easing.type: Easing.InCubic
|
||||||
alwaysRunToEnd: true
|
properties: "opacity"
|
||||||
|
target: input
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
id: input
|
id: input
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
palette: Nheko.colors
|
|
||||||
color: labelC.color
|
color: labelC.color
|
||||||
opacity: labelC.text ? 0 : 1
|
|
||||||
focus: true
|
focus: true
|
||||||
|
opacity: labelC.text ? 0 : 1
|
||||||
onTextEdited: c.textEdited()
|
|
||||||
onAccepted: c.accepted()
|
|
||||||
onEditingFinished: c.editingFinished()
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
id: backgroundRect
|
id: backgroundRect
|
||||||
|
@ -129,44 +113,46 @@ ColumnLayout {
|
||||||
color: labelC.text ? "transparent" : backgroundColor
|
color: labelC.text ? "transparent" : backgroundColor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onAccepted: c.accepted()
|
||||||
|
onEditingFinished: c.editingFinished()
|
||||||
|
onTextEdited: c.textEdited()
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: clearText
|
id: clearText
|
||||||
|
|
||||||
|
focusPolicy: Qt.NoFocus
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/round-remove-button.svg"
|
||||||
visible: c.hasClear && searchField.text !== ''
|
visible: c.hasClear && searchField.text !== ''
|
||||||
|
|
||||||
image: ":/icons/icons/ui/round-remove-button.svg"
|
|
||||||
focusPolicy: Qt.NoFocus
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
searchField.clear()
|
searchField.clear();
|
||||||
topBar.searchString = "";
|
topBar.searchString = "";
|
||||||
}
|
}
|
||||||
hoverEnabled: true
|
|
||||||
anchors {
|
anchors {
|
||||||
top: parent.top
|
|
||||||
bottom: parent.bottom
|
bottom: parent.bottom
|
||||||
right: parent.right
|
right: parent.right
|
||||||
rightMargin: Nheko.paddingSmall
|
rightMargin: Nheko.paddingSmall
|
||||||
|
top: parent.top
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: blueBar
|
id: blueBar
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
color: palette.highlight
|
||||||
color: Nheko.colors.highlight
|
|
||||||
height: 1
|
height: 1
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: blackBar
|
id: blackBar
|
||||||
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
height: parent.height*2
|
anchors.top: parent.top
|
||||||
|
color: palette.text
|
||||||
|
height: parent.height * 2
|
||||||
width: 0
|
width: 0
|
||||||
color: Nheko.colors.text
|
|
||||||
|
|
||||||
states: State {
|
states: State {
|
||||||
name: "focused"
|
name: "focused"
|
||||||
|
@ -176,31 +162,25 @@ ColumnLayout {
|
||||||
target: blackBar
|
target: blackBar
|
||||||
width: blueBar.width
|
width: blueBar.width
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
transitions: Transition {
|
transitions: Transition {
|
||||||
from: ""
|
from: ""
|
||||||
to: "focused"
|
|
||||||
reversible: true
|
reversible: true
|
||||||
|
to: "focused"
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: blackBar
|
alwaysRunToEnd: true
|
||||||
properties: "width"
|
|
||||||
duration: 310
|
duration: 310
|
||||||
easing.type: Easing.InCubic
|
easing.type: Easing.InCubic
|
||||||
alwaysRunToEnd: true
|
properties: "width"
|
||||||
|
target: blackBar
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: hover
|
id: hover
|
||||||
|
|
||||||
enabled: c.ToolTip.text
|
enabled: c.ToolTip.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,60 +14,54 @@ import im.nheko 1.0
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: inputBar
|
id: inputBar
|
||||||
|
|
||||||
|
property bool showAllButtons: width > 450 || (messageInput.length == 0 && !messageInput.inputMethodComposing)
|
||||||
readonly property string text: messageInput.text
|
readonly property string text: messageInput.text
|
||||||
|
|
||||||
color: Nheko.colors.window
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: row.implicitHeight
|
|
||||||
Layout.minimumHeight: 40
|
Layout.minimumHeight: 40
|
||||||
property bool showAllButtons: width > 450 || (messageInput.length == 0 && !messageInput.inputMethodComposing)
|
Layout.preferredHeight: row.implicitHeight
|
||||||
|
color: palette.window
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: placeCallDialog
|
id: placeCallDialog
|
||||||
|
|
||||||
PlaceCall {
|
PlaceCall {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: screenShareDialog
|
id: screenShareDialog
|
||||||
|
|
||||||
ScreenShare {
|
ScreenShare {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: row
|
id: row
|
||||||
|
|
||||||
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
visible: CallManager.callsSupported && showAllButtons
|
|
||||||
opacity: (CallManager.haveCallInvite || CallManager.isOnCallOnOtherDevice) ? 0.3 : 1
|
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
hoverEnabled: true
|
|
||||||
width: 22
|
|
||||||
height: 22
|
|
||||||
image: CallManager.isOnCall ? ":/icons/icons/ui/end-call.svg" : ":/icons/icons/ui/place-call.svg"
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: CallManager.isOnCall ? qsTr("Hang up") : (CallManager.isOnCallOnOtherDevice ? qsTr("Already on a call") : qsTr("Place a call"))
|
|
||||||
Layout.margins: 8
|
Layout.margins: 8
|
||||||
|
ToolTip.text: CallManager.isOnCall ? qsTr("Hang up") : (CallManager.isOnCallOnOtherDevice ? qsTr("Already on a call") : qsTr("Place a call"))
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
height: 22
|
||||||
|
hoverEnabled: true
|
||||||
|
image: CallManager.isOnCall ? ":/icons/icons/ui/end-call.svg" : ":/icons/icons/ui/place-call.svg"
|
||||||
|
opacity: (CallManager.haveCallInvite || CallManager.isOnCallOnOtherDevice) ? 0.3 : 1
|
||||||
|
visible: CallManager.callsSupported && showAllButtons
|
||||||
|
width: 22
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (room) {
|
if (room) {
|
||||||
if (CallManager.haveCallInvite) {
|
if (CallManager.haveCallInvite) {
|
||||||
return ;
|
return;
|
||||||
} else if (CallManager.isOnCall) {
|
} else if (CallManager.isOnCall) {
|
||||||
CallManager.hangUp();
|
CallManager.hangUp();
|
||||||
}
|
} else if (CallManager.isOnCallOnOtherDevice) {
|
||||||
else if(CallManager.isOnCallOnOtherDevice) {
|
|
||||||
return;
|
return;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
var dialog = placeCallDialog.createObject(timelineRoot);
|
var dialog = placeCallDialog.createObject(timelineRoot);
|
||||||
dialog.open();
|
dialog.open();
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
|
@ -75,22 +69,22 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
visible: showAllButtons
|
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
hoverEnabled: true
|
|
||||||
width: 22
|
|
||||||
height: 22
|
|
||||||
image: ":/icons/icons/ui/attach.svg"
|
|
||||||
Layout.margins: 8
|
Layout.margins: 8
|
||||||
onClicked: room.input.openFileSelection()
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Send a file")
|
ToolTip.text: qsTr("Send a file")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
height: 22
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/attach.svg"
|
||||||
|
visible: showAllButtons
|
||||||
|
width: 22
|
||||||
|
|
||||||
|
onClicked: room.input.openFileSelection()
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: Nheko.colors.window
|
color: palette.window
|
||||||
visible: room && room.input.uploading
|
visible: room && room.input.uploading
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
|
@ -98,112 +92,67 @@ Rectangle {
|
||||||
height: parent.height / 2
|
height: parent.height / 2
|
||||||
running: parent.visible
|
running: parent.visible
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id: textInput
|
id: textInput
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
Layout.maximumHeight: Window.height / 4
|
Layout.maximumHeight: Window.height / 4
|
||||||
Layout.minimumHeight: fontMetrics.lineSpacing
|
Layout.minimumHeight: fontMetrics.lineSpacing
|
||||||
Layout.preferredHeight: contentHeight
|
Layout.preferredHeight: contentHeight
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
|
||||||
contentWidth: availableWidth
|
contentWidth: availableWidth
|
||||||
|
|
||||||
TextArea {
|
TextArea {
|
||||||
id: messageInput
|
id: messageInput
|
||||||
|
|
||||||
property int completerTriggeredAt: 0
|
property int completerTriggeredAt: 0
|
||||||
|
property string lastChar
|
||||||
|
|
||||||
function insertCompletion(completion) {
|
function insertCompletion(completion) {
|
||||||
messageInput.remove(completerTriggeredAt, cursorPosition);
|
messageInput.remove(completerTriggeredAt, cursorPosition);
|
||||||
messageInput.insert(cursorPosition, completion);
|
messageInput.insert(cursorPosition, completion);
|
||||||
}
|
}
|
||||||
|
|
||||||
function openCompleter(pos, type) {
|
function openCompleter(pos, type) {
|
||||||
if (popup.opened) return;
|
if (popup.opened)
|
||||||
|
return;
|
||||||
completerTriggeredAt = pos;
|
completerTriggeredAt = pos;
|
||||||
completer.completerName = type;
|
completer.completerName = type;
|
||||||
popup.open();
|
popup.open();
|
||||||
completer.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition)+messageInput.preeditText);
|
completer.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition) + messageInput.preeditText);
|
||||||
}
|
}
|
||||||
|
|
||||||
function positionCursorAtEnd() {
|
function positionCursorAtEnd() {
|
||||||
cursorPosition = messageInput.length;
|
cursorPosition = messageInput.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
function positionCursorAtStart() {
|
function positionCursorAtStart() {
|
||||||
cursorPosition = 0;
|
cursorPosition = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
selectByMouse: true
|
background: null
|
||||||
placeholderText: qsTr("Write a message...")
|
|
||||||
placeholderTextColor: Nheko.colors.buttonText
|
|
||||||
color: Nheko.colors.text
|
|
||||||
width: textInput.width
|
|
||||||
verticalAlignment: TextEdit.AlignVCenter
|
|
||||||
wrapMode: TextEdit.Wrap
|
|
||||||
padding: 0
|
|
||||||
topPadding: 8
|
|
||||||
bottomPadding: 8
|
bottomPadding: 8
|
||||||
leftPadding: inputBar.showAllButtons? 0 : 8
|
color: palette.text
|
||||||
focus: true
|
focus: true
|
||||||
property string lastChar
|
leftPadding: inputBar.showAllButtons ? 0 : 8
|
||||||
onTextChanged: {
|
padding: 0
|
||||||
if (room)
|
placeholderText: qsTr("Write a message...")
|
||||||
room.input.updateState(selectionStart, selectionEnd, cursorPosition, text);
|
placeholderTextColor: palette.buttonText
|
||||||
forceActiveFocus();
|
selectByMouse: true
|
||||||
if (cursorPosition > 0)
|
topPadding: 8
|
||||||
lastChar = text.charAt(cursorPosition-1)
|
verticalAlignment: TextEdit.AlignVCenter
|
||||||
else
|
width: textInput.width
|
||||||
lastChar = ''
|
wrapMode: TextEdit.Wrap
|
||||||
if (lastChar == '@') {
|
|
||||||
messageInput.openCompleter(selectionStart-1, "user");
|
|
||||||
} else if (lastChar == ':') {
|
|
||||||
messageInput.openCompleter(selectionStart-1, "emoji");
|
|
||||||
} else if (lastChar == '#') {
|
|
||||||
messageInput.openCompleter(selectionStart-1, "roomAliases");
|
|
||||||
} else if (lastChar == "/" && cursorPosition == 1) {
|
|
||||||
messageInput.openCompleter(selectionStart-1, "command");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onCursorPositionChanged: {
|
|
||||||
if (!room)
|
|
||||||
return ;
|
|
||||||
|
|
||||||
room.input.updateState(selectionStart, selectionEnd, cursorPosition, text);
|
Keys.onPressed: event => {
|
||||||
if (popup.opened && cursorPosition <= completerTriggeredAt)
|
|
||||||
popup.close();
|
|
||||||
|
|
||||||
if (popup.opened)
|
|
||||||
completer.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition)+messageInput.preeditText);
|
|
||||||
|
|
||||||
}
|
|
||||||
onPreeditTextChanged: {
|
|
||||||
if (popup.opened)
|
|
||||||
completer.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition)+messageInput.preeditText);
|
|
||||||
}
|
|
||||||
onSelectionStartChanged: room.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
|
|
||||||
onSelectionEndChanged: room.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
|
|
||||||
// Ensure that we get escape key press events first.
|
|
||||||
Keys.onShortcutOverride: event.accepted = (popup.opened && (event.key === Qt.Key_Escape || event.key === Qt.Key_Tab || event.key === Qt.Key_Enter || event.key === Qt.Key_Space))
|
|
||||||
Keys.onPressed: {
|
|
||||||
if (event.matches(StandardKey.Paste)) {
|
if (event.matches(StandardKey.Paste)) {
|
||||||
event.accepted = room.input.tryPasteAttachment(false);
|
event.accepted = room.input.tryPasteAttachment(false);
|
||||||
} else if (event.key == Qt.Key_Space) {
|
} else if (event.key == Qt.Key_Space) {
|
||||||
// close popup if user enters space after colon
|
// close popup if user enters space after colon
|
||||||
if (cursorPosition == completerTriggeredAt + 1)
|
if (cursorPosition == completerTriggeredAt + 1)
|
||||||
popup.close();
|
popup.close();
|
||||||
|
|
||||||
if (popup.opened && completer.count <= 0)
|
if (popup.opened && completer.count <= 0)
|
||||||
popup.close();
|
popup.close();
|
||||||
|
|
||||||
} else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_U) {
|
} else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_U) {
|
||||||
messageInput.clear();
|
messageInput.clear();
|
||||||
} else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_P) {
|
} else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_P) {
|
||||||
|
@ -218,8 +167,8 @@ Rectangle {
|
||||||
completer.completerName = "";
|
completer.completerName = "";
|
||||||
popup.close();
|
popup.close();
|
||||||
} else if (event.matches(StandardKey.InsertLineSeparator)) {
|
} else if (event.matches(StandardKey.InsertLineSeparator)) {
|
||||||
if (popup.opened) popup.close();
|
if (popup.opened)
|
||||||
|
popup.close();
|
||||||
if (Settings.invertEnterKey && (!Qt.inputMethod.visible || Qt.platform.os === "windows")) {
|
if (Settings.invertEnterKey && (!Qt.inputMethod.visible || Qt.platform.os === "windows")) {
|
||||||
room.input.send();
|
room.input.send();
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
|
@ -253,16 +202,16 @@ Rectangle {
|
||||||
console.log('"' + t + '"');
|
console.log('"' + t + '"');
|
||||||
if (t == '@') {
|
if (t == '@') {
|
||||||
messageInput.openCompleter(pos, "user");
|
messageInput.openCompleter(pos, "user");
|
||||||
return ;
|
return;
|
||||||
} else if (t == ' ' || t == '\t') {
|
} else if (t == ' ' || t == '\t') {
|
||||||
messageInput.openCompleter(pos + 1, "user");
|
messageInput.openCompleter(pos + 1, "user");
|
||||||
return ;
|
return;
|
||||||
} else if (t == ':') {
|
} else if (t == ':') {
|
||||||
messageInput.openCompleter(pos, "emoji");
|
messageInput.openCompleter(pos, "emoji");
|
||||||
return ;
|
return;
|
||||||
} else if (t == '~') {
|
} else if (t == '~') {
|
||||||
messageInput.openCompleter(pos, "customEmoji");
|
messageInput.openCompleter(pos, "customEmoji");
|
||||||
return ;
|
return;
|
||||||
}
|
}
|
||||||
pos = pos - 1;
|
pos = pos - 1;
|
||||||
}
|
}
|
||||||
|
@ -312,21 +261,53 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
background: null
|
// Ensure that we get escape key press events first.
|
||||||
|
Keys.onShortcutOverride: event => event.accepted = (popup.opened && (event.key === Qt.Key_Escape || event.key === Qt.Key_Tab || event.key === Qt.Key_Enter || event.key === Qt.Key_Space))
|
||||||
|
onCursorPositionChanged: {
|
||||||
|
if (!room)
|
||||||
|
return;
|
||||||
|
room.input.updateState(selectionStart, selectionEnd, cursorPosition, text);
|
||||||
|
if (popup.opened && cursorPosition <= completerTriggeredAt)
|
||||||
|
popup.close();
|
||||||
|
if (popup.opened)
|
||||||
|
completer.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition) + messageInput.preeditText);
|
||||||
|
}
|
||||||
|
onPreeditTextChanged: {
|
||||||
|
if (popup.opened)
|
||||||
|
completer.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition) + messageInput.preeditText);
|
||||||
|
}
|
||||||
|
onSelectionEndChanged: room.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
|
||||||
|
onSelectionStartChanged: room.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
|
||||||
|
onTextChanged: {
|
||||||
|
if (room)
|
||||||
|
room.input.updateState(selectionStart, selectionEnd, cursorPosition, text);
|
||||||
|
forceActiveFocus();
|
||||||
|
if (cursorPosition > 0)
|
||||||
|
lastChar = text.charAt(cursorPosition - 1);
|
||||||
|
else
|
||||||
|
lastChar = '';
|
||||||
|
if (lastChar == '@') {
|
||||||
|
messageInput.openCompleter(selectionStart - 1, "user");
|
||||||
|
} else if (lastChar == ':') {
|
||||||
|
messageInput.openCompleter(selectionStart - 1, "emoji");
|
||||||
|
} else if (lastChar == '#') {
|
||||||
|
messageInput.openCompleter(selectionStart - 1, "roomAliases");
|
||||||
|
} else if (lastChar == "/" && cursorPosition == 1) {
|
||||||
|
messageInput.openCompleter(selectionStart - 1, "command");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onRoomChanged() {
|
function onRoomChanged() {
|
||||||
messageInput.clear();
|
messageInput.clear();
|
||||||
if (room)
|
if (room)
|
||||||
messageInput.append(room.input.text);
|
messageInput.append(room.input.text);
|
||||||
|
|
||||||
completer.completerName = "";
|
completer.completerName = "";
|
||||||
messageInput.forceActiveFocus();
|
messageInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
target: timelineView
|
target: timelineView
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onCompletionClicked(completion) {
|
function onCompletionClicked(completion) {
|
||||||
messageInput.insertCompletion(completion);
|
messageInput.insertCompletion(completion);
|
||||||
|
@ -334,43 +315,39 @@ Rectangle {
|
||||||
|
|
||||||
target: completer
|
target: completer
|
||||||
}
|
}
|
||||||
|
|
||||||
Popup {
|
Popup {
|
||||||
id: popup
|
id: popup
|
||||||
|
|
||||||
|
background: null
|
||||||
|
padding: 0
|
||||||
x: messageInput.positionToRectangle(messageInput.completerTriggeredAt).x
|
x: messageInput.positionToRectangle(messageInput.completerTriggeredAt).x
|
||||||
y: messageInput.positionToRectangle(messageInput.completerTriggeredAt).y - height
|
y: messageInput.positionToRectangle(messageInput.completerTriggeredAt).y - height
|
||||||
|
|
||||||
background: null
|
enter: Transition {
|
||||||
padding: 0
|
NumberAnimation {
|
||||||
|
duration: 100
|
||||||
|
from: 0
|
||||||
|
property: "opacity"
|
||||||
|
to: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 100
|
||||||
|
from: 1
|
||||||
|
property: "opacity"
|
||||||
|
to: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Completer {
|
Completer {
|
||||||
anchors.fill: parent
|
|
||||||
id: completer
|
id: completer
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
rowMargin: 2
|
rowMargin: 2
|
||||||
rowSpacing: 0
|
rowSpacing: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
enter: Transition {
|
|
||||||
NumberAnimation {
|
|
||||||
property: "opacity"
|
|
||||||
from: 0
|
|
||||||
to: 1
|
|
||||||
duration: 100
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
exit: Transition {
|
|
||||||
NumberAnimation {
|
|
||||||
property: "opacity"
|
|
||||||
from: 1
|
|
||||||
to: 0
|
|
||||||
duration: 100
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onTextChanged(newText) {
|
function onTextChanged(newText) {
|
||||||
messageInput.text = newText;
|
messageInput.text = newText;
|
||||||
|
@ -380,16 +357,13 @@ Rectangle {
|
||||||
ignoreUnknownSignals: true
|
ignoreUnknownSignals: true
|
||||||
target: room ? room.input : null
|
target: room ? room.input : null
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onReplyChanged() {
|
|
||||||
messageInput.forceActiveFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onEditChanged() {
|
function onEditChanged() {
|
||||||
messageInput.forceActiveFocus();
|
messageInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
|
function onReplyChanged() {
|
||||||
|
messageInput.forceActiveFocus();
|
||||||
|
}
|
||||||
function onThreadChanged() {
|
function onThreadChanged() {
|
||||||
messageInput.forceActiveFocus();
|
messageInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
|
@ -397,7 +371,6 @@ Rectangle {
|
||||||
ignoreUnknownSignals: true
|
ignoreUnknownSignals: true
|
||||||
target: room
|
target: room
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onFocusInput() {
|
function onFocusInput() {
|
||||||
messageInput.forceActiveFocus();
|
messageInput.forceActiveFocus();
|
||||||
|
@ -405,91 +378,82 @@ Rectangle {
|
||||||
|
|
||||||
target: TimelineManager
|
target: TimelineManager
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
acceptedButtons: Qt.MiddleButton
|
||||||
// workaround for wrong cursor shape on some platforms
|
// workaround for wrong cursor shape on some platforms
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.MiddleButton
|
|
||||||
cursorShape: Qt.IBeamCursor
|
cursorShape: Qt.IBeamCursor
|
||||||
onPressed: (mouse) => mouse.accepted = room.input.tryPasteAttachment(true)
|
|
||||||
|
onPressed: mouse => mouse.accepted = room.input.tryPasteAttachment(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: stickerButton
|
id: stickerButton
|
||||||
visible: showAllButtons
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
||||||
Layout.margins: 8
|
Layout.margins: 8
|
||||||
hoverEnabled: true
|
|
||||||
width: 22
|
|
||||||
height: 22
|
|
||||||
image: ":/icons/icons/ui/sticky-note-solid.svg"
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Stickers")
|
ToolTip.text: qsTr("Stickers")
|
||||||
onClicked: stickerPopup.visible ? stickerPopup.close() : stickerPopup.show(stickerButton, room.roomId, function(row) {
|
ToolTip.visible: hovered
|
||||||
room.input.sticker(row);
|
height: 22
|
||||||
TimelineManager.focusMessageInput();
|
hoverEnabled: true
|
||||||
})
|
image: ":/icons/icons/ui/sticky-note-solid.svg"
|
||||||
|
visible: showAllButtons
|
||||||
|
width: 22
|
||||||
|
|
||||||
|
onClicked: stickerPopup.visible ? stickerPopup.close() : stickerPopup.show(stickerButton, room.roomId, function (row) {
|
||||||
|
room.input.sticker(row);
|
||||||
|
TimelineManager.focusMessageInput();
|
||||||
|
})
|
||||||
|
|
||||||
StickerPicker {
|
StickerPicker {
|
||||||
id: stickerPopup
|
id: stickerPopup
|
||||||
|
|
||||||
colors: Nheko.colors
|
|
||||||
emoji: false
|
emoji: false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: emojiButton
|
id: emojiButton
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
||||||
Layout.margins: 8
|
Layout.margins: 8
|
||||||
hoverEnabled: true
|
|
||||||
width: 22
|
|
||||||
height: 22
|
|
||||||
image: ":/icons/icons/ui/smile.svg"
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Emoji")
|
ToolTip.text: qsTr("Emoji")
|
||||||
onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(emojiButton, room.roomId, function(plaintext, markdown) {
|
ToolTip.visible: hovered
|
||||||
messageInput.insert(messageInput.cursorPosition, markdown);
|
height: 22
|
||||||
TimelineManager.focusMessageInput();
|
hoverEnabled: true
|
||||||
})
|
image: ":/icons/icons/ui/smile.svg"
|
||||||
|
width: 22
|
||||||
|
|
||||||
|
onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(emojiButton, room.roomId, function (plaintext, markdown) {
|
||||||
|
messageInput.insert(messageInput.cursorPosition, markdown);
|
||||||
|
TimelineManager.focusMessageInput();
|
||||||
|
})
|
||||||
|
|
||||||
StickerPicker {
|
StickerPicker {
|
||||||
id: emojiPopup
|
id: emojiPopup
|
||||||
|
|
||||||
colors: Nheko.colors
|
|
||||||
emoji: true
|
emoji: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
||||||
Layout.margins: 8
|
Layout.margins: 8
|
||||||
hoverEnabled: true
|
|
||||||
width: 22
|
|
||||||
height: 22
|
|
||||||
image: ":/icons/icons/ui/send.svg"
|
|
||||||
Layout.rightMargin: 8
|
Layout.rightMargin: 8
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Send")
|
ToolTip.text: qsTr("Send")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
height: 22
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/send.svg"
|
||||||
|
width: 22
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
room.input.send();
|
room.input.send();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
visible: room ? (!room.permissions.canSend(MtxEvent.TextMessage)) : false
|
|
||||||
text: qsTr("You don't have permission to send messages in this room")
|
text: qsTr("You don't have permission to send messages in this room")
|
||||||
color: Nheko.colors.text
|
visible: room ? (!room.permissions.canSend(MtxEvent.TextMessage)) : false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,38 +10,35 @@ import im.nheko 1.0
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: warningRoot
|
id: warningRoot
|
||||||
|
|
||||||
required property string text
|
|
||||||
property color bubbleColor: Nheko.theme.error
|
property color bubbleColor: Nheko.theme.error
|
||||||
|
required property string text
|
||||||
|
|
||||||
implicitHeight: visible ? warningDisplay.implicitHeight + 4 * Nheko.paddingSmall : 0
|
|
||||||
height: implicitHeight
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: Nheko.colors.window // required to hide the timeline behind this warning
|
color: palette.window // required to hide the timeline behind this warning
|
||||||
|
height: implicitHeight
|
||||||
|
implicitHeight: visible ? warningDisplay.implicitHeight + 4 * Nheko.paddingSmall : 0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: warningRect
|
id: warningRect
|
||||||
|
|
||||||
visible: warningRoot.visible
|
|
||||||
// TODO: Qt.alpha() would make more sense but it wasn't working...
|
|
||||||
color: Qt.rgba(bubbleColor.r, bubbleColor.g, bubbleColor.b, 0.3)
|
|
||||||
border.width: 1
|
|
||||||
border.color: bubbleColor
|
|
||||||
radius: 3
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: visible ? Nheko.paddingSmall : 0
|
anchors.margins: visible ? Nheko.paddingSmall : 0
|
||||||
|
border.color: bubbleColor
|
||||||
|
border.width: 1
|
||||||
|
// TODO: Qt.alpha() would make more sense but it wasn't working...
|
||||||
|
color: Qt.rgba(bubbleColor.r, bubbleColor.g, bubbleColor.b, 0.3)
|
||||||
|
radius: 3
|
||||||
|
visible: warningRoot.visible
|
||||||
z: 3
|
z: 3
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: warningDisplay
|
id: warningDisplay
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.margins: Nheko.paddingSmall
|
anchors.margins: Nheko.paddingSmall
|
||||||
color: Nheko.colors.text
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: warningRoot.text
|
text: warningRoot.text
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,18 +2,17 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtGraphicalEffects 1.0
|
import QtQuick
|
||||||
import QtQuick 2.12
|
import QtQuick.Window
|
||||||
import QtQuick.Window 2.2
|
import im.nheko
|
||||||
import im.nheko 1.0
|
import QtQuick.Effects
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: privacyScreen
|
id: privacyScreen
|
||||||
|
|
||||||
readonly property bool active: Settings.privacyScreen && screenSaver.state === "Visible"
|
readonly property bool active: Settings.privacyScreen && screenSaver.state === "Visible"
|
||||||
property var timelineRoot
|
|
||||||
property int screenTimeout
|
property int screenTimeout
|
||||||
|
property var timelineRoot
|
||||||
required property var windowTarget
|
required property var windowTarget
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
@ -24,29 +23,28 @@ Item {
|
||||||
} else {
|
} else {
|
||||||
if (timelineRoot.visible)
|
if (timelineRoot.visible)
|
||||||
screenSaverTimer.start();
|
screenSaverTimer.start();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
target: windowTarget
|
target: windowTarget
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: screenSaverTimer
|
id: screenSaverTimer
|
||||||
|
|
||||||
interval: screenTimeout * 1000
|
interval: screenTimeout * 1000
|
||||||
running: !windowTarget.active
|
running: !windowTarget.active
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
screenSaver.state = "Visible";
|
screenSaver.state = "Visible";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: screenSaver
|
id: screenSaver
|
||||||
|
|
||||||
state: "Invisible"
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
state: "Invisible"
|
||||||
visible: false
|
visible: false
|
||||||
|
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
name: "Visible"
|
name: "Visible"
|
||||||
|
@ -55,20 +53,18 @@ Item {
|
||||||
target: screenSaver
|
target: screenSaver
|
||||||
visible: true
|
visible: true
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: screenSaver
|
|
||||||
opacity: 1
|
opacity: 1
|
||||||
|
target: screenSaver
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "Invisible"
|
name: "Invisible"
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: screenSaver
|
|
||||||
opacity: 0
|
opacity: 0
|
||||||
|
target: screenSaver
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: screenSaver
|
target: screenSaver
|
||||||
visible: false
|
visible: false
|
||||||
|
@ -78,36 +74,33 @@ Item {
|
||||||
transitions: [
|
transitions: [
|
||||||
Transition {
|
Transition {
|
||||||
from: "Invisible"
|
from: "Invisible"
|
||||||
to: "Visible"
|
|
||||||
reversible: true
|
reversible: true
|
||||||
|
to: "Visible"
|
||||||
|
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: screenSaver
|
|
||||||
property: "visible"
|
|
||||||
duration: 0
|
duration: 0
|
||||||
}
|
property: "visible"
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
target: screenSaver
|
target: screenSaver
|
||||||
property: "opacity"
|
}
|
||||||
|
NumberAnimation {
|
||||||
duration: 300
|
duration: 300
|
||||||
easing.type: Easing.Linear
|
easing.type: Easing.Linear
|
||||||
|
property: "opacity"
|
||||||
|
target: screenSaver
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
FastBlur {
|
MultiEffect {
|
||||||
id: blur
|
id: blur
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
blur: 1.0
|
||||||
|
blurEnabled: true
|
||||||
|
blurMax: 32
|
||||||
source: timelineRoot
|
source: timelineRoot
|
||||||
radius: 50
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,84 +11,83 @@ Popup {
|
||||||
id: quickSwitcher
|
id: quickSwitcher
|
||||||
|
|
||||||
property int textHeight: Math.round(Qt.application.font.pixelSize * 2.4)
|
property int textHeight: Math.round(Qt.application.font.pixelSize * 2.4)
|
||||||
|
property int textMargin: Nheko.paddingSmall
|
||||||
|
|
||||||
background: null
|
background: null
|
||||||
width: Math.min(Math.max(Math.round(parent.width / 2),450),parent.width) // limiting width to parent.width/2 can be a bit narrow
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||||
|
modal: true
|
||||||
|
|
||||||
|
// Workaround palettes not inheriting for popups
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
parent: Overlay.overlay
|
||||||
|
width: Math.min(Math.max(Math.round(parent.width / 2), 450), parent.width) // limiting width to parent.width/2 can be a bit narrow
|
||||||
x: Math.round(parent.width / 2 - contentWidth / 2)
|
x: Math.round(parent.width / 2 - contentWidth / 2)
|
||||||
y: Math.round(parent.height / 4)
|
y: Math.round(parent.height / 4)
|
||||||
modal: true
|
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
Overlay.modal: Rectangle {
|
||||||
parent: Overlay.overlay
|
color: "#aa1E1E1E"
|
||||||
palette: Nheko.colors
|
}
|
||||||
|
|
||||||
|
onClosed: TimelineManager.focusMessageInput()
|
||||||
onOpened: {
|
onOpened: {
|
||||||
roomTextInput.forceActiveFocus();
|
roomTextInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
onClosed: TimelineManager.focusMessageInput()
|
|
||||||
property int textMargin: Nheko.paddingSmall
|
|
||||||
|
|
||||||
Column{
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 1
|
spacing: 1
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: roomTextInput
|
id: roomTextInput
|
||||||
|
|
||||||
width: parent.width
|
color: palette.text
|
||||||
font.pixelSize: Math.ceil(quickSwitcher.textHeight * 0.6)
|
font.pixelSize: Math.ceil(quickSwitcher.textHeight * 0.6)
|
||||||
color: Nheko.colors.text
|
width: parent.width
|
||||||
onTextEdited: {
|
|
||||||
completerPopup.completer.searchString = text;
|
Keys.onPressed: event => {
|
||||||
}
|
|
||||||
Keys.onPressed: {
|
|
||||||
if (event.key == Qt.Key_Up || event.key == Qt.Key_Backtab) {
|
if (event.key == Qt.Key_Up || event.key == Qt.Key_Backtab) {
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
completerPopup.up();
|
completerPopup.up();
|
||||||
} else if (event.key == Qt.Key_Down || event.key == Qt.Key_Tab) {
|
} else if (event.key == Qt.Key_Down || event.key == Qt.Key_Tab) {
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
if (event.key == Qt.Key_Tab && (event.modifiers & Qt.ShiftModifier))
|
if (event.key == Qt.Key_Tab && (event.modifiers & Qt.ShiftModifier))
|
||||||
completerPopup.up();
|
completerPopup.up();
|
||||||
else
|
else
|
||||||
completerPopup.down();
|
completerPopup.down();
|
||||||
} else if (event.matches(StandardKey.InsertParagraphSeparator)) {
|
} else if (event.matches(StandardKey.InsertParagraphSeparator)) {
|
||||||
completerPopup.finishCompletion();
|
completerPopup.finishCompletion();
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onTextEdited: {
|
||||||
|
completerPopup.completer.searchString = text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Completer {
|
Completer {
|
||||||
id: completerPopup
|
id: completerPopup
|
||||||
|
|
||||||
visible: roomTextInput.text.length > 0
|
|
||||||
width: parent.width
|
|
||||||
completerName: "room"
|
|
||||||
bottomToTop: false
|
|
||||||
fullWidth: true
|
|
||||||
avatarHeight: quickSwitcher.textHeight
|
avatarHeight: quickSwitcher.textHeight
|
||||||
avatarWidth: quickSwitcher.textHeight
|
avatarWidth: quickSwitcher.textHeight
|
||||||
|
bottomToTop: false
|
||||||
centerRowContent: false
|
centerRowContent: false
|
||||||
|
completerName: "room"
|
||||||
|
fullWidth: true
|
||||||
rowMargin: Math.round(quickSwitcher.textMargin / 2)
|
rowMargin: Math.round(quickSwitcher.textMargin / 2)
|
||||||
rowSpacing: quickSwitcher.textMargin
|
rowSpacing: quickSwitcher.textMargin
|
||||||
|
visible: roomTextInput.text.length > 0
|
||||||
|
width: parent.width
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onCompletionSelected(id) {
|
function onCompletionSelected(id) {
|
||||||
Rooms.setCurrentRoom(id);
|
Rooms.setCurrentRoom(id);
|
||||||
quickSwitcher.close();
|
quickSwitcher.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCountChanged() {
|
function onCountChanged() {
|
||||||
if (completerPopup.count > 0 && (completerPopup.currentIndex < 0 || completerPopup.currentIndex >= completerPopup.count))
|
if (completerPopup.count > 0 && (completerPopup.currentIndex < 0 || completerPopup.currentIndex >= completerPopup.count))
|
||||||
completerPopup.currentIndex = 0;
|
completerPopup.currentIndex = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target: completerPopup
|
target: completerPopup
|
||||||
}
|
}
|
||||||
|
|
||||||
Overlay.modal: Rectangle {
|
|
||||||
color: "#aa1E1E1E"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,11 @@ import im.nheko 1.0
|
||||||
Flow {
|
Flow {
|
||||||
id: reactionFlow
|
id: reactionFlow
|
||||||
|
|
||||||
// lower-contrast colors to avoid distracting from text & to enhance hover effect
|
|
||||||
property color gentleHighlight: Qt.hsla(Nheko.colors.highlight.hslHue, Nheko.colors.highlight.hslSaturation, Nheko.colors.highlight.hslLightness, 0.8)
|
|
||||||
property color gentleText: Qt.hsla(Nheko.colors.text.hslHue, Nheko.colors.text.hslSaturation, Nheko.colors.text.hslLightness, 0.6)
|
|
||||||
property string eventId
|
property string eventId
|
||||||
|
|
||||||
|
// lower-contrast colors to avoid distracting from text & to enhance hover effect
|
||||||
|
property color gentleHighlight: Qt.hsla(palette.highlight.hslHue, palette.highlight.hslSaturation, palette.highlight.hslLightness, 0.8)
|
||||||
|
property color gentleText: Qt.hsla(palette.text.hslHue, palette.text.hslSaturation, palette.text.hslLightness, 0.6)
|
||||||
property alias reactions: repeater.model
|
property alias reactions: repeater.model
|
||||||
|
|
||||||
spacing: 4
|
spacing: 4
|
||||||
|
@ -25,40 +26,39 @@ Flow {
|
||||||
delegate: AbstractButton {
|
delegate: AbstractButton {
|
||||||
id: reaction
|
id: reaction
|
||||||
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
onClicked: {
|
ToolTip.visible: hovered
|
||||||
console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + ". selfReactedEvent: " + modelData.selfReactedEvent);
|
hoverEnabled: true
|
||||||
room.input.reaction(reactionFlow.eventId, modelData.key);
|
leftPadding: textMetrics.height / 2
|
||||||
}
|
rightPadding: textMetrics.height / 2
|
||||||
Component.onCompleted: {
|
|
||||||
ToolTip.text = Qt.binding(function() {
|
|
||||||
if (textMetrics.elidedText === textMetrics.text) {
|
|
||||||
return modelData.users;
|
|
||||||
}
|
|
||||||
return modelData.displayKey + "\n" + modelData.users;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
leftPadding: textMetrics.height / 2
|
|
||||||
rightPadding: textMetrics.height / 2
|
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
border.color: reaction.hovered ? palette.text : gentleText
|
||||||
|
border.width: 1
|
||||||
|
color: reaction.hovered ? palette.highlight : (modelData.selfReactedEvent !== '' ? gentleHighlight : palette.window)
|
||||||
|
implicitHeight: reaction.implicitHeight
|
||||||
|
implicitWidth: reaction.implicitWidth
|
||||||
|
radius: reaction.height / 2
|
||||||
|
}
|
||||||
contentItem: Row {
|
contentItem: Row {
|
||||||
spacing: textMetrics.height / 4
|
spacing: textMetrics.height / 4
|
||||||
|
|
||||||
TextMetrics {
|
TextMetrics {
|
||||||
id: textMetrics
|
id: textMetrics
|
||||||
|
|
||||||
font.family: Settings.emojiFont
|
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
elideWidth: 150
|
elideWidth: 150
|
||||||
|
font.family: Settings.emojiFont
|
||||||
text: modelData.displayKey
|
text: modelData.displayKey
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: reactionText
|
id: reactionText
|
||||||
|
|
||||||
anchors.baseline: reactionCounter.baseline
|
anchors.baseline: reactionCounter.baseline
|
||||||
|
color: (reaction.hovered || modelData.selfReactedEvent !== '') ? palette.highlightedText : palette.text
|
||||||
|
font.family: Settings.emojiFont
|
||||||
|
maximumLineCount: 1
|
||||||
text: {
|
text: {
|
||||||
// When an emoji font is selected that doesn't have …, it is dropped from elidedText. So we add it back.
|
// When an emoji font is selected that doesn't have …, it is dropped from elidedText. So we add it back.
|
||||||
if (textMetrics.elidedText !== modelData.displayKey) {
|
if (textMetrics.elidedText !== modelData.displayKey) {
|
||||||
|
@ -68,51 +68,45 @@ Flow {
|
||||||
}
|
}
|
||||||
return textMetrics.elidedText;
|
return textMetrics.elidedText;
|
||||||
}
|
}
|
||||||
font.family: Settings.emojiFont
|
|
||||||
color: (reaction.hovered || modelData.selfReactedEvent !== '') ? Nheko.colors.highlightedText: Nheko.colors.text
|
|
||||||
maximumLineCount: 1
|
|
||||||
visible: !modelData.key.startsWith("mxc://")
|
visible: !modelData.key.startsWith("mxc://")
|
||||||
}
|
}
|
||||||
Image {
|
Image {
|
||||||
anchors.verticalCenter: divider.verticalCenter
|
anchors.verticalCenter: divider.verticalCenter
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
height: textMetrics.height
|
height: textMetrics.height
|
||||||
width: textMetrics.height
|
|
||||||
source: modelData.key.startsWith("mxc://") ? (modelData.key.replace("mxc://", "image://MxcImage/") + "?scale") : ""
|
source: modelData.key.startsWith("mxc://") ? (modelData.key.replace("mxc://", "image://MxcImage/") + "?scale") : ""
|
||||||
visible: modelData.key.startsWith("mxc://")
|
visible: modelData.key.startsWith("mxc://")
|
||||||
fillMode: Image.PreserveAspectFit
|
width: textMetrics.height
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: divider
|
id: divider
|
||||||
|
|
||||||
|
color: reaction.hovered ? palette.text : gentleText
|
||||||
height: Math.floor(reactionCounter.implicitHeight * 1.4)
|
height: Math.floor(reactionCounter.implicitHeight * 1.4)
|
||||||
width: 1
|
width: 1
|
||||||
color: reaction.hovered ? Nheko.colors.text: gentleText
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: reactionCounter
|
id: reactionCounter
|
||||||
|
|
||||||
anchors.verticalCenter: divider.verticalCenter
|
anchors.verticalCenter: divider.verticalCenter
|
||||||
text: modelData.count
|
color: (reaction.hovered || modelData.selfReactedEvent !== '') ? palette.highlightedText : palette.windowText
|
||||||
font: reaction.font
|
font: reaction.font
|
||||||
color: (reaction.hovered || modelData.selfReactedEvent !== '') ? Nheko.colors.highlightedText: Nheko.colors.windowText
|
text: modelData.count
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
Component.onCompleted: {
|
||||||
anchors.centerIn: parent
|
ToolTip.text = Qt.binding(function () {
|
||||||
implicitWidth: reaction.implicitWidth
|
if (textMetrics.elidedText === textMetrics.text) {
|
||||||
implicitHeight: reaction.implicitHeight
|
return modelData.users;
|
||||||
border.color: reaction.hovered ? Nheko.colors.text: gentleText
|
}
|
||||||
color: reaction.hovered ? Nheko.colors.highlight : (modelData.selfReactedEvent !== '' ? gentleHighlight : Nheko.colors.window)
|
return modelData.displayKey + "\n" + modelData.users;
|
||||||
border.width: 1
|
});
|
||||||
radius: reaction.height / 2
|
}
|
||||||
|
onClicked: {
|
||||||
|
console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + ". selfReactedEvent: " + modelData.selfReactedEvent);
|
||||||
|
room.input.reaction(reactionFlow.eventId, modelData.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,91 +12,89 @@ Rectangle {
|
||||||
id: replyPopup
|
id: replyPopup
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
visible: room && (room.reply || room.edit || room.thread)
|
color: palette.window
|
||||||
// Height of child, plus margins, plus border
|
// Height of child, plus margins, plus border
|
||||||
implicitHeight: (room && room.reply ? replyPreview.height : Math.max(closeEditButton.height, closeThreadButton.height)) + Nheko.paddingSmall
|
implicitHeight: (room && room.reply ? replyPreview.height : Math.max(closeEditButton.height, closeThreadButton.height)) + Nheko.paddingSmall
|
||||||
color: Nheko.colors.window
|
visible: room && (room.reply || room.edit || room.thread)
|
||||||
z: 3
|
z: 3
|
||||||
|
|
||||||
Reply {
|
Reply {
|
||||||
id: replyPreview
|
id: replyPreview
|
||||||
|
|
||||||
property var modelData: room ? room.getDump(room.reply, room.id) : {
|
property var modelData: room ? room.getDump(room.reply, room.id) : {}
|
||||||
}
|
|
||||||
|
|
||||||
visible: room && room.reply
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: replyPopup.width < 450? Nheko.paddingSmall : (CallManager.callsSupported? 2*(22+16) : 1*(22+16))
|
anchors.leftMargin: replyPopup.width < 450 ? Nheko.paddingSmall : (CallManager.callsSupported ? 2 * (22 + 16) : 1 * (22 + 16))
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: replyPopup.width < 450? 2*(22+16) : 3*(22+16)
|
anchors.rightMargin: replyPopup.width < 450 ? 2 * (22 + 16) : 3 * (22 + 16)
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: Nheko.paddingSmall
|
anchors.topMargin: Nheko.paddingSmall
|
||||||
userColor: TimelineManager.userColor(modelData.userId, Nheko.colors.window)
|
|
||||||
blurhash: modelData.blurhash ?? ""
|
blurhash: modelData.blurhash ?? ""
|
||||||
body: modelData.body ?? ""
|
body: modelData.body ?? ""
|
||||||
formattedBody: modelData.formattedBody ?? ""
|
encryptionError: modelData.encryptionError ?? 0
|
||||||
eventId: modelData.eventId ?? ""
|
eventId: modelData.eventId ?? ""
|
||||||
filename: modelData.filename ?? ""
|
filename: modelData.filename ?? ""
|
||||||
filesize: modelData.filesize ?? ""
|
filesize: modelData.filesize ?? ""
|
||||||
|
formattedBody: modelData.formattedBody ?? ""
|
||||||
|
isOnlyEmoji: modelData.isOnlyEmoji ?? false
|
||||||
|
originalWidth: modelData.originalWidth ?? 0
|
||||||
proportionalHeight: modelData.proportionalHeight ?? 1
|
proportionalHeight: modelData.proportionalHeight ?? 1
|
||||||
type: modelData.type ?? MtxEvent.UnknownMessage
|
type: modelData.type ?? MtxEvent.UnknownMessage
|
||||||
typeString: modelData.typeString ?? ""
|
typeString: modelData.typeString ?? ""
|
||||||
url: modelData.url ?? ""
|
url: modelData.url ?? ""
|
||||||
originalWidth: modelData.originalWidth ?? 0
|
userColor: TimelineManager.userColor(modelData.userId, palette.window)
|
||||||
isOnlyEmoji: modelData.isOnlyEmoji ?? false
|
|
||||||
userId: modelData.userId ?? ""
|
userId: modelData.userId ?? ""
|
||||||
userName: modelData.userName ?? ""
|
userName: modelData.userName ?? ""
|
||||||
encryptionError: modelData.encryptionError ?? ""
|
visible: room && room.reply
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: closeReplyButton
|
id: closeReplyButton
|
||||||
|
|
||||||
visible: room && room.reply
|
ToolTip.text: qsTr("Close")
|
||||||
|
ToolTip.visible: closeReplyButton.hovered
|
||||||
|
anchors.margins: Nheko.paddingSmall
|
||||||
anchors.right: replyPreview.right
|
anchors.right: replyPreview.right
|
||||||
anchors.top: replyPreview.top
|
anchors.top: replyPreview.top
|
||||||
anchors.margins: Nheko.paddingSmall
|
|
||||||
hoverEnabled: true
|
|
||||||
width: 16
|
|
||||||
height: 16
|
height: 16
|
||||||
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/dismiss.svg"
|
image: ":/icons/icons/ui/dismiss.svg"
|
||||||
ToolTip.visible: closeReplyButton.hovered
|
visible: room && room.reply
|
||||||
ToolTip.text: qsTr("Close")
|
width: 16
|
||||||
|
|
||||||
onClicked: room.reply = undefined
|
onClicked: room.reply = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: closeEditButton
|
id: closeEditButton
|
||||||
|
|
||||||
visible: room && room.edit
|
ToolTip.text: qsTr("Cancel Edit")
|
||||||
anchors.right: closeThreadButton.left
|
ToolTip.visible: closeEditButton.hovered
|
||||||
anchors.margins: 8
|
anchors.margins: 8
|
||||||
|
anchors.right: closeThreadButton.left
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
|
height: 22
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/dismiss_edit.svg"
|
image: ":/icons/icons/ui/dismiss_edit.svg"
|
||||||
|
visible: room && room.edit
|
||||||
width: 22
|
width: 22
|
||||||
height: 22
|
|
||||||
ToolTip.visible: closeEditButton.hovered
|
|
||||||
ToolTip.text: qsTr("Cancel Edit")
|
|
||||||
onClicked: room.edit = undefined
|
onClicked: room.edit = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: closeThreadButton
|
id: closeThreadButton
|
||||||
|
|
||||||
visible: room && room.thread
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.margins: 8
|
|
||||||
anchors.top: parent.top
|
|
||||||
hoverEnabled: true
|
|
||||||
buttonTextColor: room ? TimelineManager.userColor(room.thread, Nheko.colors.base) : Nheko.colors.buttonText
|
|
||||||
image: ":/icons/icons/ui/dismiss_thread.svg"
|
|
||||||
width: 22
|
|
||||||
height: 22
|
|
||||||
ToolTip.visible: closeThreadButton.hovered
|
|
||||||
ToolTip.text: qsTr("Cancel Thread")
|
ToolTip.text: qsTr("Cancel Thread")
|
||||||
|
ToolTip.visible: closeThreadButton.hovered
|
||||||
|
anchors.margins: 8
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
buttonTextColor: room ? TimelineManager.userColor(room.thread, palette.base) : palette.buttonText
|
||||||
|
height: 22
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/dismiss_thread.svg"
|
||||||
|
visible: room && room.thread
|
||||||
|
width: 22
|
||||||
|
|
||||||
onClicked: room.thread = undefined
|
onClicked: room.thread = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -10,30 +10,23 @@ import "./pages"
|
||||||
import "./voip"
|
import "./voip"
|
||||||
import "./ui"
|
import "./ui"
|
||||||
import Qt.labs.platform 1.1 as Platform
|
import Qt.labs.platform 1.1 as Platform
|
||||||
import QtQuick 2.15
|
import QtQuick
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts
|
||||||
import QtQuick.Window 2.15
|
import QtQuick.Window
|
||||||
import im.nheko 1.0
|
import im.nheko
|
||||||
import im.nheko.EmojiModel 1.0
|
|
||||||
|
|
||||||
Pane {
|
Pane {
|
||||||
id: timelineRoot
|
id: timelineRoot
|
||||||
|
|
||||||
palette: Nheko.colors
|
function destroyOnClose(obj) {
|
||||||
background: null
|
if (obj.closing != undefined)
|
||||||
padding: 0
|
obj.closing.connect(() => obj.destroy(1000));
|
||||||
|
else if (obj.aboutToHide != undefined)
|
||||||
FontMetrics {
|
obj.aboutToHide.connect(() => obj.destroy(1000));
|
||||||
id: fontMetrics
|
|
||||||
}
|
}
|
||||||
|
function destroyOnClosed(obj) {
|
||||||
RoomDirectoryModel {
|
obj.aboutToHide.connect(() => obj.destroy(1000));
|
||||||
id: publicRooms
|
|
||||||
}
|
|
||||||
|
|
||||||
UserDirectoryModel {
|
|
||||||
id: userDirectory
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Timer {
|
//Timer {
|
||||||
|
@ -42,54 +35,49 @@ Pane {
|
||||||
// running: true
|
// running: true
|
||||||
// repeat: true
|
// repeat: true
|
||||||
//}
|
//}
|
||||||
|
|
||||||
function showAliasEditor(settings) {
|
function showAliasEditor(settings) {
|
||||||
var component = Qt.createComponent("qrc:/qml/dialogs/AliasEditor.qml")
|
var component = Qt.createComponent("qrc:/resources/qml/dialogs/AliasEditor.qml");
|
||||||
if (component.status == Component.Ready) {
|
if (component.status == Component.Ready) {
|
||||||
var dialog = component.createObject(timelineRoot, {
|
var dialog = component.createObject(timelineRoot, {
|
||||||
"roomSettings": settings
|
"roomSettings": settings
|
||||||
});
|
});
|
||||||
dialog.show();
|
|
||||||
destroyOnClose(dialog);
|
|
||||||
} else {
|
|
||||||
console.error("Failed to create component: " + component.errorString());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function showPLEditor(settings) {
|
|
||||||
var component = Qt.createComponent("qrc:/qml/dialogs/PowerLevelEditor.qml")
|
|
||||||
if (component.status == Component.Ready) {
|
|
||||||
var dialog = component.createObject(timelineRoot, {
|
|
||||||
"roomSettings": settings
|
|
||||||
});
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
destroyOnClose(dialog);
|
destroyOnClose(dialog);
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to create component: " + component.errorString());
|
console.error("Failed to create component: " + component.errorString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showSpacePLApplyPrompt(settings, editingModel) {
|
|
||||||
var component = Qt.createComponent("qrc:/qml/dialogs/PowerLevelSpacesApplyDialog.qml")
|
|
||||||
if (component.status == Component.Ready) {
|
|
||||||
var dialog = component.createObject(timelineRoot, {
|
|
||||||
"roomSettings": settings,
|
|
||||||
"editingModel": editingModel
|
|
||||||
});
|
|
||||||
dialog.show();
|
|
||||||
destroyOnClose(dialog);
|
|
||||||
} else {
|
|
||||||
console.error("Failed to create component: " + component.errorString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showAllowedRoomsEditor(settings) {
|
function showAllowedRoomsEditor(settings) {
|
||||||
var component = Qt.createComponent("qrc:/qml/dialogs/AllowedRoomsSettingsDialog.qml")
|
var component = Qt.createComponent("qrc:/resources/qml/dialogs/AllowedRoomsSettingsDialog.qml");
|
||||||
if (component.status == Component.Ready) {
|
if (component.status == Component.Ready) {
|
||||||
var dialog = component.createObject(timelineRoot, {
|
var dialog = component.createObject(timelineRoot, {
|
||||||
"roomSettings": settings
|
"roomSettings": settings
|
||||||
});
|
});
|
||||||
|
dialog.show();
|
||||||
|
destroyOnClose(dialog);
|
||||||
|
} else {
|
||||||
|
console.error("Failed to create component: " + component.errorString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function showPLEditor(settings) {
|
||||||
|
var component = Qt.createComponent("qrc:/resources/qml/dialogs/PowerLevelEditor.qml");
|
||||||
|
if (component.status == Component.Ready) {
|
||||||
|
var dialog = component.createObject(timelineRoot, {
|
||||||
|
"roomSettings": settings
|
||||||
|
});
|
||||||
|
dialog.show();
|
||||||
|
destroyOnClose(dialog);
|
||||||
|
} else {
|
||||||
|
console.error("Failed to create component: " + component.errorString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function showSpacePLApplyPrompt(settings, editingModel) {
|
||||||
|
var component = Qt.createComponent("qrc:/resources/qml/dialogs/PowerLevelSpacesApplyDialog.qml");
|
||||||
|
if (component.status == Component.Ready) {
|
||||||
|
var dialog = component.createObject(timelineRoot, {
|
||||||
|
"roomSettings": settings,
|
||||||
|
"editingModel": editingModel
|
||||||
|
});
|
||||||
dialog.show();
|
dialog.show();
|
||||||
destroyOnClose(dialog);
|
destroyOnClose(dialog);
|
||||||
} else {
|
} else {
|
||||||
|
@ -97,23 +85,37 @@ Pane {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
background: null
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
FontMetrics {
|
||||||
|
id: fontMetrics
|
||||||
|
|
||||||
|
}
|
||||||
|
UserDirectoryModel {
|
||||||
|
id: userDirectory
|
||||||
|
|
||||||
|
}
|
||||||
|
RoomDirectoryModel {
|
||||||
|
id: publicRooms
|
||||||
|
|
||||||
|
}
|
||||||
Component {
|
Component {
|
||||||
id: readReceiptsDialog
|
id: readReceiptsDialog
|
||||||
|
|
||||||
ReadReceipts {
|
ReadReceipts {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Quit
|
sequence: StandardKey.Quit
|
||||||
|
|
||||||
onActivated: Qt.quit()
|
onActivated: Qt.quit()
|
||||||
}
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: "Ctrl+K"
|
sequence: "Ctrl+K"
|
||||||
|
|
||||||
onActivated: {
|
onActivated: {
|
||||||
var component = Qt.createComponent("qrc:/qml/QuickSwitcher.qml")
|
var component = Qt.createComponent("qrc:/resources/qml/QuickSwitcher.qml");
|
||||||
if (component.status == Component.Ready) {
|
if (component.status == Component.Ready) {
|
||||||
var quickSwitch = component.createObject(timelineRoot);
|
var quickSwitch = component.createObject(timelineRoot);
|
||||||
quickSwitch.open();
|
quickSwitch.open();
|
||||||
|
@ -123,37 +125,25 @@ Pane {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
// Add alternative shortcut, because sometimes Alt+A is stolen by the TextEdit
|
// Add alternative shortcut, because sometimes Alt+A is stolen by the TextEdit
|
||||||
sequences: ["Alt+A", "Ctrl+Shift+A"]
|
sequences: ["Alt+A", "Ctrl+Shift+A"]
|
||||||
|
|
||||||
onActivated: Rooms.nextRoomWithActivity()
|
onActivated: Rooms.nextRoomWithActivity()
|
||||||
}
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: "Ctrl+Down"
|
sequence: "Ctrl+Down"
|
||||||
|
|
||||||
onActivated: Rooms.nextRoom()
|
onActivated: Rooms.nextRoom()
|
||||||
}
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: "Ctrl+Up"
|
sequence: "Ctrl+Up"
|
||||||
|
|
||||||
onActivated: Rooms.previousRoom()
|
onActivated: Rooms.previousRoom()
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onOpenLogoutDialog() {
|
|
||||||
var component = Qt.createComponent("qrc:/qml/dialogs/LogoutDialog.qml")
|
|
||||||
if (component.status == Component.Ready) {
|
|
||||||
var dialog = component.createObject(timelineRoot);
|
|
||||||
dialog.open();
|
|
||||||
destroyOnClose(dialog);
|
|
||||||
} else {
|
|
||||||
console.error("Failed to create component: " + component.errorString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onOpenJoinRoomDialog() {
|
function onOpenJoinRoomDialog() {
|
||||||
var component = Qt.createComponent("qrc:/qml/dialogs/JoinRoomDialog.qml")
|
var component = Qt.createComponent("qrc:/resources/qml/dialogs/JoinRoomDialog.qml");
|
||||||
if (component.status == Component.Ready) {
|
if (component.status == Component.Ready) {
|
||||||
var dialog = component.createObject(timelineRoot);
|
var dialog = component.createObject(timelineRoot);
|
||||||
dialog.show();
|
dialog.show();
|
||||||
|
@ -162,11 +152,22 @@ Pane {
|
||||||
console.error("Failed to create component: " + component.errorString());
|
console.error("Failed to create component: " + component.errorString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function onOpenLogoutDialog() {
|
||||||
function onShowRoomJoinPrompt(summary) {
|
var component = Qt.createComponent("qrc:/resources/qml/dialogs/LogoutDialog.qml");
|
||||||
var component = Qt.createComponent("qrc:/qml/dialogs/ConfirmJoinRoomDialog.qml")
|
|
||||||
if (component.status == Component.Ready) {
|
if (component.status == Component.Ready) {
|
||||||
var dialog = component.createObject(timelineRoot, {"summary": summary});
|
var dialog = component.createObject(timelineRoot);
|
||||||
|
dialog.open();
|
||||||
|
destroyOnClose(dialog);
|
||||||
|
} else {
|
||||||
|
console.error("Failed to create component: " + component.errorString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function onShowRoomJoinPrompt(summary) {
|
||||||
|
var component = Qt.createComponent("qrc:/resources/qml/dialogs/ConfirmJoinRoomDialog.qml");
|
||||||
|
if (component.status == Component.Ready) {
|
||||||
|
var dialog = component.createObject(timelineRoot, {
|
||||||
|
"summary": summary
|
||||||
|
});
|
||||||
dialog.show();
|
dialog.show();
|
||||||
destroyOnClose(dialog);
|
destroyOnClose(dialog);
|
||||||
} else {
|
} else {
|
||||||
|
@ -176,12 +177,13 @@ Pane {
|
||||||
|
|
||||||
target: Nheko
|
target: Nheko
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onNewDeviceVerificationRequest(flow) {
|
function onNewDeviceVerificationRequest(flow) {
|
||||||
var component = Qt.createComponent("qrc:/qml/device-verification/DeviceVerification.qml")
|
var component = Qt.createComponent("qrc:/resources/qml/device-verification/DeviceVerification.qml");
|
||||||
if (component.status == Component.Ready) {
|
if (component.status == Component.Ready) {
|
||||||
var dialog = component.createObject(timelineRoot, {"flow": flow});
|
var dialog = component.createObject(timelineRoot, {
|
||||||
|
"flow": flow
|
||||||
|
});
|
||||||
dialog.show();
|
dialog.show();
|
||||||
destroyOnClose(dialog);
|
destroyOnClose(dialog);
|
||||||
} else {
|
} else {
|
||||||
|
@ -191,101 +193,71 @@ Pane {
|
||||||
|
|
||||||
target: VerificationManager
|
target: VerificationManager
|
||||||
}
|
}
|
||||||
|
|
||||||
function destroyOnClose(obj) {
|
|
||||||
if (obj.closing != undefined) obj.closing.connect(() => obj.destroy(1000));
|
|
||||||
else if (obj.aboutToHide != undefined) obj.aboutToHide.connect(() => obj.destroy(1000));
|
|
||||||
}
|
|
||||||
|
|
||||||
function destroyOnClosed(obj) {
|
|
||||||
obj.aboutToHide.connect(() => obj.destroy(1000));
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onOpenProfile(profile) {
|
|
||||||
var component = Qt.createComponent("qrc:/qml/dialogs/UserProfile.qml")
|
|
||||||
if (component.status == Component.Ready) {
|
|
||||||
var userProfile = component.createObject(timelineRoot, {"profile": profile});
|
|
||||||
userProfile.show();
|
|
||||||
destroyOnClose(userProfile);
|
|
||||||
} else {
|
|
||||||
console.error("Failed to create component: " + component.errorString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onShowImagePackSettings(room, packlist) {
|
|
||||||
var component = Qt.createComponent("qrc:/qml/dialogs/ImagePackSettingsDialog.qml")
|
|
||||||
|
|
||||||
if (component.status == Component.Ready) {
|
|
||||||
var packSet = component.createObject(timelineRoot, {
|
|
||||||
"room": room,
|
|
||||||
"packlist": packlist
|
|
||||||
});
|
|
||||||
packSet.show();
|
|
||||||
destroyOnClose(packSet);
|
|
||||||
} else {
|
|
||||||
console.error("Failed to create component: " + component.errorString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onOpenRoomMembersDialog(members, room) {
|
|
||||||
var component = Qt.createComponent("qrc:/qml/dialogs/RoomMembers.qml")
|
|
||||||
if (component.status == Component.Ready) {
|
|
||||||
var membersDialog = component.createObject(timelineRoot, {
|
|
||||||
"members": members,
|
|
||||||
"room": room
|
|
||||||
});
|
|
||||||
membersDialog.show();
|
|
||||||
destroyOnClose(membersDialog);
|
|
||||||
} else {
|
|
||||||
console.error("Failed to create component: " + component.errorString());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function onOpenRoomSettingsDialog(settings) {
|
|
||||||
var component = Qt.createComponent("qrc:/qml/dialogs/RoomSettings.qml")
|
|
||||||
if (component.status == Component.Ready) {
|
|
||||||
var roomSettings = component.createObject(timelineRoot, {
|
|
||||||
"roomSettings": settings
|
|
||||||
});
|
|
||||||
roomSettings.show();
|
|
||||||
destroyOnClose(roomSettings);
|
|
||||||
} else {
|
|
||||||
console.error("Failed to create component: " + component.errorString());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function onOpenInviteUsersDialog(invitees) {
|
function onOpenInviteUsersDialog(invitees) {
|
||||||
var component = Qt.createComponent("qrc:/qml/dialogs/InviteDialog.qml")
|
var component = Qt.createComponent("qrc:/resources/qml/dialogs/InviteDialog.qml");
|
||||||
if (component.status == Component.Ready) {
|
if (component.status == Component.Ready) {
|
||||||
var dialog = component.createObject(timelineRoot, {
|
var dialog = component.createObject(timelineRoot, {
|
||||||
"invitees": invitees
|
"invitees": invitees
|
||||||
});
|
});
|
||||||
dialog.show();
|
dialog.show();
|
||||||
destroyOnClose(dialog);
|
destroyOnClose(dialog);
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to create component: " + component.errorString());
|
console.error("Failed to create component: " + component.errorString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onOpenLeaveRoomDialog(roomid, reason) {
|
function onOpenLeaveRoomDialog(roomid, reason) {
|
||||||
var component = Qt.createComponent("qrc:/qml/dialogs/LeaveRoomDialog.qml")
|
var component = Qt.createComponent("qrc:/resources/qml/dialogs/LeaveRoomDialog.qml");
|
||||||
if (component.status == Component.Ready) {
|
if (component.status == Component.Ready) {
|
||||||
var dialog = component.createObject(timelineRoot, {
|
var dialog = component.createObject(timelineRoot, {
|
||||||
"roomId": roomid,
|
"roomId": roomid,
|
||||||
"reason": reason
|
"reason": reason
|
||||||
});
|
});
|
||||||
dialog.open();
|
dialog.open();
|
||||||
destroyOnClose(dialog);
|
destroyOnClose(dialog);
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to create component: " + component.errorString());
|
console.error("Failed to create component: " + component.errorString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function onOpenProfile(profile) {
|
||||||
|
var component = Qt.createComponent("qrc:/resources/qml/dialogs/UserProfile.qml");
|
||||||
|
if (component.status == Component.Ready) {
|
||||||
|
var userProfile = component.createObject(timelineRoot, {
|
||||||
|
"profile": profile
|
||||||
|
});
|
||||||
|
userProfile.show();
|
||||||
|
destroyOnClose(userProfile);
|
||||||
|
} else {
|
||||||
|
console.error("Failed to create component: " + component.errorString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function onOpenRoomMembersDialog(members, room) {
|
||||||
|
var component = Qt.createComponent("qrc:/resources/qml/dialogs/RoomMembers.qml");
|
||||||
|
if (component.status == Component.Ready) {
|
||||||
|
var membersDialog = component.createObject(timelineRoot, {
|
||||||
|
"members": members,
|
||||||
|
"room": room
|
||||||
|
});
|
||||||
|
membersDialog.show();
|
||||||
|
destroyOnClose(membersDialog);
|
||||||
|
} else {
|
||||||
|
console.error("Failed to create component: " + component.errorString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function onOpenRoomSettingsDialog(settings) {
|
||||||
|
var component = Qt.createComponent("qrc:/resources/qml/dialogs/RoomSettings.qml");
|
||||||
|
if (component.status == Component.Ready) {
|
||||||
|
var roomSettings = component.createObject(timelineRoot, {
|
||||||
|
"roomSettings": settings
|
||||||
|
});
|
||||||
|
roomSettings.show();
|
||||||
|
destroyOnClose(roomSettings);
|
||||||
|
} else {
|
||||||
|
console.error("Failed to create component: " + component.errorString());
|
||||||
|
}
|
||||||
|
}
|
||||||
function onShowImageOverlay(room, eventId, url, originalWidth, proportionalHeight) {
|
function onShowImageOverlay(room, eventId, url, originalWidth, proportionalHeight) {
|
||||||
var component = Qt.createComponent("qrc:/qml/dialogs/ImageOverlay.qml")
|
var component = Qt.createComponent("qrc:/resources/qml/dialogs/ImageOverlay.qml");
|
||||||
if (component.status == Component.Ready) {
|
if (component.status == Component.Ready) {
|
||||||
var dialog = component.createObject(timelineRoot, {
|
var dialog = component.createObject(timelineRoot, {
|
||||||
"room": room,
|
"room": room,
|
||||||
|
@ -293,22 +265,33 @@ Pane {
|
||||||
"url": url,
|
"url": url,
|
||||||
"originalWidth": originalWidth ?? 0,
|
"originalWidth": originalWidth ?? 0,
|
||||||
"proportionalHeight": proportionalHeight ?? 0
|
"proportionalHeight": proportionalHeight ?? 0
|
||||||
}
|
});
|
||||||
);
|
|
||||||
dialog.showFullScreen();
|
dialog.showFullScreen();
|
||||||
destroyOnClose(dialog);
|
destroyOnClose(dialog);
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to create component: " + component.errorString());
|
console.error("Failed to create component: " + component.errorString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function onShowImagePackSettings(room, packlist) {
|
||||||
|
var component = Qt.createComponent("qrc:/resources/qml/dialogs/ImagePackSettingsDialog.qml");
|
||||||
|
if (component.status == Component.Ready) {
|
||||||
|
var packSet = component.createObject(timelineRoot, {
|
||||||
|
"room": room,
|
||||||
|
"packlist": packlist
|
||||||
|
});
|
||||||
|
packSet.show();
|
||||||
|
destroyOnClose(packSet);
|
||||||
|
} else {
|
||||||
|
console.error("Failed to create component: " + component.errorString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
target: TimelineManager
|
target: TimelineManager
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onNewInviteState() {
|
function onNewInviteState() {
|
||||||
if (CallManager.haveCallInvite && Settings.mobileMode) {
|
if (CallManager.haveCallInvite && Settings.mobileMode) {
|
||||||
var component = Qt.createComponent("qrc:/qml/voip/CallInvite.qml")
|
var component = Qt.createComponent("qrc:/resources/qml/voip/CallInvite.qml");
|
||||||
if (component.status == Component.Ready) {
|
if (component.status == Component.Ready) {
|
||||||
var dialog = component.createObject(timelineRoot);
|
var dialog = component.createObject(timelineRoot);
|
||||||
dialog.open();
|
dialog.open();
|
||||||
|
@ -321,141 +304,97 @@ Pane {
|
||||||
|
|
||||||
target: CallManager
|
target: CallManager
|
||||||
}
|
}
|
||||||
|
|
||||||
SelfVerificationCheck {
|
SelfVerificationCheck {
|
||||||
}
|
}
|
||||||
|
|
||||||
InputDialog {
|
InputDialog {
|
||||||
id: uiaPassPrompt
|
id: uiaPassPrompt
|
||||||
|
|
||||||
echoMode: TextInput.Password
|
echoMode: TextInput.Password
|
||||||
title: UIA.title
|
|
||||||
prompt: qsTr("Please enter your login password to continue:")
|
prompt: qsTr("Please enter your login password to continue:")
|
||||||
onAccepted: (t) => {
|
title: UIA.title
|
||||||
|
|
||||||
|
onAccepted: t => {
|
||||||
return UIA.continuePassword(t);
|
return UIA.continuePassword(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InputDialog {
|
InputDialog {
|
||||||
id: uiaEmailPrompt
|
id: uiaEmailPrompt
|
||||||
|
|
||||||
title: UIA.title
|
|
||||||
prompt: qsTr("Please enter a valid email address to continue:")
|
prompt: qsTr("Please enter a valid email address to continue:")
|
||||||
onAccepted: (t) => {
|
title: UIA.title
|
||||||
|
|
||||||
|
onAccepted: t => {
|
||||||
return UIA.continueEmail(t);
|
return UIA.continueEmail(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PhoneNumberInputDialog {
|
PhoneNumberInputDialog {
|
||||||
id: uiaPhoneNumberPrompt
|
id: uiaPhoneNumberPrompt
|
||||||
|
|
||||||
title: UIA.title
|
|
||||||
prompt: qsTr("Please enter a valid phone number to continue:")
|
prompt: qsTr("Please enter a valid phone number to continue:")
|
||||||
|
title: UIA.title
|
||||||
|
|
||||||
onAccepted: (p, t) => {
|
onAccepted: (p, t) => {
|
||||||
return UIA.continuePhoneNumber(p, t);
|
return UIA.continuePhoneNumber(p, t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InputDialog {
|
InputDialog {
|
||||||
id: uiaTokenPrompt
|
id: uiaTokenPrompt
|
||||||
|
|
||||||
title: UIA.title
|
|
||||||
prompt: qsTr("Please enter the token which has been sent to you:")
|
prompt: qsTr("Please enter the token which has been sent to you:")
|
||||||
onAccepted: (t) => {
|
title: UIA.title
|
||||||
|
|
||||||
|
onAccepted: t => {
|
||||||
return UIA.submit3pidToken(t);
|
return UIA.submit3pidToken(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MessageDialog {
|
Platform.MessageDialog {
|
||||||
id: uiaErrorDialog
|
id: uiaErrorDialog
|
||||||
|
|
||||||
buttons: Platform.MessageDialog.Ok
|
buttons: Platform.MessageDialog.Ok
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MessageDialog {
|
Platform.MessageDialog {
|
||||||
id: uiaConfirmationLinkDialog
|
id: uiaConfirmationLinkDialog
|
||||||
|
|
||||||
buttons: Platform.MessageDialog.Ok
|
buttons: Platform.MessageDialog.Ok
|
||||||
text: qsTr("Wait for the confirmation link to arrive, then continue.")
|
text: qsTr("Wait for the confirmation link to arrive, then continue.")
|
||||||
|
|
||||||
onAccepted: UIA.continue3pidReceived()
|
onAccepted: UIA.continue3pidReceived()
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onPassword() {
|
|
||||||
console.log("UIA: password needed");
|
|
||||||
uiaPassPrompt.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onEmail() {
|
|
||||||
uiaEmailPrompt.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onPhoneNumber() {
|
|
||||||
uiaPhoneNumberPrompt.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onPrompt3pidToken() {
|
|
||||||
uiaTokenPrompt.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onConfirm3pidToken() {
|
function onConfirm3pidToken() {
|
||||||
uiaConfirmationLinkDialog.open();
|
uiaConfirmationLinkDialog.open();
|
||||||
}
|
}
|
||||||
|
function onEmail() {
|
||||||
|
uiaEmailPrompt.show();
|
||||||
|
}
|
||||||
function onError(msg) {
|
function onError(msg) {
|
||||||
uiaErrorDialog.text = msg;
|
uiaErrorDialog.text = msg;
|
||||||
uiaErrorDialog.open();
|
uiaErrorDialog.open();
|
||||||
}
|
}
|
||||||
|
function onPassword() {
|
||||||
|
console.log("UIA: password needed");
|
||||||
|
uiaPassPrompt.show();
|
||||||
|
}
|
||||||
|
function onPhoneNumber() {
|
||||||
|
uiaPhoneNumberPrompt.show();
|
||||||
|
}
|
||||||
|
function onPrompt3pidToken() {
|
||||||
|
uiaTokenPrompt.show();
|
||||||
|
}
|
||||||
|
|
||||||
target: UIA
|
target: UIA
|
||||||
}
|
}
|
||||||
|
|
||||||
StackView {
|
StackView {
|
||||||
id: mainWindow
|
id: mainWindow
|
||||||
|
|
||||||
anchors.fill: parent
|
property Transition popEnterOrg
|
||||||
initialItem: welcomePage
|
property Transition popExitOrg
|
||||||
|
|
||||||
Transition {
|
|
||||||
id: reducedMotionTransitionExit
|
|
||||||
PropertyAnimation {
|
|
||||||
property: "opacity"
|
|
||||||
from: 1
|
|
||||||
to:0
|
|
||||||
duration: 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Transition {
|
|
||||||
id: reducedMotionTransitionEnter
|
|
||||||
SequentialAnimation {
|
|
||||||
PropertyAction { property: "opacity"; value: 0 }
|
|
||||||
PauseAnimation { duration: 200 }
|
|
||||||
PropertyAnimation {
|
|
||||||
property: "opacity"
|
|
||||||
from: 0
|
|
||||||
to:1
|
|
||||||
duration: 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// for some reason direct bindings to a hidden StackView don't work, so manually store and restore here.
|
// for some reason direct bindings to a hidden StackView don't work, so manually store and restore here.
|
||||||
property Transition pushEnterOrg
|
property Transition pushEnterOrg
|
||||||
property Transition pushExitOrg
|
property Transition pushExitOrg
|
||||||
property Transition popEnterOrg
|
|
||||||
property Transition popExitOrg
|
|
||||||
property Transition replaceEnterOrg
|
property Transition replaceEnterOrg
|
||||||
property Transition replaceExitOrg
|
property Transition replaceExitOrg
|
||||||
Component.onCompleted: {
|
|
||||||
pushEnterOrg = pushEnter;
|
|
||||||
popEnterOrg = popEnter;
|
|
||||||
replaceEnterOrg = replaceEnter;
|
|
||||||
pushExitOrg = pushExit;
|
|
||||||
popExitOrg = popExit;
|
|
||||||
replaceExitOrg = replaceExit;
|
|
||||||
|
|
||||||
updateTrans()
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateTrans() {
|
function updateTrans() {
|
||||||
pushEnter = Settings.reducedMotion ? reducedMotionTransitionEnter : pushEnterOrg;
|
pushEnter = Settings.reducedMotion ? reducedMotionTransitionEnter : pushEnterOrg;
|
||||||
|
@ -466,65 +405,104 @@ Pane {
|
||||||
replaceExit = Settings.reducedMotion ? reducedMotionTransitionExit : replaceExitOrg;
|
replaceExit = Settings.reducedMotion ? reducedMotionTransitionExit : replaceExitOrg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
initialItem: welcomePage
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
pushEnterOrg = pushEnter;
|
||||||
|
popEnterOrg = popEnter;
|
||||||
|
replaceEnterOrg = replaceEnter;
|
||||||
|
pushExitOrg = pushExit;
|
||||||
|
popExitOrg = popExit;
|
||||||
|
replaceExitOrg = replaceExit;
|
||||||
|
updateTrans();
|
||||||
|
}
|
||||||
|
|
||||||
|
Transition {
|
||||||
|
id: reducedMotionTransitionExit
|
||||||
|
|
||||||
|
PropertyAnimation {
|
||||||
|
duration: 200
|
||||||
|
from: 1
|
||||||
|
property: "opacity"
|
||||||
|
to: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Transition {
|
||||||
|
id: reducedMotionTransitionEnter
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
PropertyAction {
|
||||||
|
property: "opacity"
|
||||||
|
value: 0
|
||||||
|
}
|
||||||
|
PauseAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
PropertyAnimation {
|
||||||
|
duration: 200
|
||||||
|
from: 0
|
||||||
|
property: "opacity"
|
||||||
|
to: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Connections {
|
Connections {
|
||||||
target: Settings
|
|
||||||
function onReducedMotionChanged() {
|
function onReducedMotionChanged() {
|
||||||
mainWindow.updateTrans();
|
mainWindow.updateTrans();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target: Settings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: welcomePage
|
id: welcomePage
|
||||||
|
|
||||||
WelcomePage {
|
WelcomePage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: chatPage
|
id: chatPage
|
||||||
|
|
||||||
ChatPage {
|
ChatPage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: loginPage
|
id: loginPage
|
||||||
|
|
||||||
LoginPage {
|
LoginPage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: registerPage
|
id: registerPage
|
||||||
|
|
||||||
RegisterPage {
|
RegisterPage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: userSettingsPage
|
id: userSettingsPage
|
||||||
|
|
||||||
UserSettingsPage {
|
UserSettingsPage {
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Snackbar {
|
||||||
|
id: snackbar
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Snackbar { id: snackbar }
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onSwitchToChatPage() {
|
|
||||||
mainWindow.replace(null, chatPage);
|
|
||||||
}
|
|
||||||
function onSwitchToLoginPage(error) {
|
|
||||||
mainWindow.replace(welcomePage, {}, loginPage, {"error": error}, StackView.PopTransition);
|
|
||||||
}
|
|
||||||
function onShowNotification(msg) {
|
function onShowNotification(msg) {
|
||||||
snackbar.showNotification(msg);
|
snackbar.showNotification(msg);
|
||||||
console.log("New snack: " + msg);
|
console.log("New snack: " + msg);
|
||||||
}
|
}
|
||||||
|
function onSwitchToChatPage() {
|
||||||
|
mainWindow.replace(null, chatPage);
|
||||||
|
}
|
||||||
|
function onSwitchToLoginPage(error) {
|
||||||
|
mainWindow.replace(welcomePage, {}, loginPage, {
|
||||||
|
"error": error
|
||||||
|
}, StackView.PopTransition);
|
||||||
|
}
|
||||||
|
|
||||||
target: MainWindow
|
target: MainWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,96 +0,0 @@
|
||||||
// Copyright (C) 2016 Michael Bohlender, <michael.bohlender@kdemail.net>
|
|
||||||
// Copyright (C) 2017 Christian Mollekopf, <mollekopf@kolabsystems.com>
|
|
||||||
// SPDX-FileCopyrightText: Nheko Contributors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Shamelessly stolen from:
|
|
||||||
* https://cgit.kde.org/kube.git/tree/framework/qml/ScrollHelper.qml
|
|
||||||
*
|
|
||||||
* The MouseArea + interactive: false + maximumFlickVelocity are required
|
|
||||||
* to fix scrolling for desktop systems where we don't want flicking behaviour.
|
|
||||||
*
|
|
||||||
* See also:
|
|
||||||
* ScrollView.qml in qtquickcontrols
|
|
||||||
* qquickwheelarea.cpp in qtquickcontrols
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.9
|
|
||||||
import QtQuick.Controls 2.3
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
// console.warn("Delta: ", wheel.pixelDelta.y);
|
|
||||||
// console.warn("Old position: ", flickable.contentY);
|
|
||||||
// console.warn("New position: ", newPos);
|
|
||||||
// breaks ListView's with headers...
|
|
||||||
//if (typeof (flickableItem.headerItem) !== "undefined" && flickableItem.headerItem)
|
|
||||||
// minYExtent += flickableItem.headerItem.height;
|
|
||||||
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property Flickable flickable
|
|
||||||
property alias enabled: root.enabled
|
|
||||||
|
|
||||||
function calculateNewPosition(flickableItem, wheel) {
|
|
||||||
//Nothing to scroll
|
|
||||||
if (flickableItem.contentHeight < flickableItem.height)
|
|
||||||
return flickableItem.contentY;
|
|
||||||
|
|
||||||
//Ignore 0 events (happens at least with Christians trackpad)
|
|
||||||
if (wheel.pixelDelta.y == 0 && wheel.angleDelta.y == 0)
|
|
||||||
return flickableItem.contentY;
|
|
||||||
|
|
||||||
//pixelDelta seems to be the same as angleDelta/8
|
|
||||||
var pixelDelta = 0;
|
|
||||||
//The pixelDelta is a smaller number if both are provided, so pixelDelta can be 0 while angleDelta is still something. So we check the angleDelta
|
|
||||||
if (wheel.angleDelta.y) {
|
|
||||||
var wheelScrollLines = 3; //Default value of QApplication wheelScrollLines property
|
|
||||||
var pixelPerLine = 20; //Default value in Qt, originally comes from QTextEdit
|
|
||||||
var ticks = (wheel.angleDelta.y / 8) / 15; //Divide by 8 gives us pixels typically come in 15pixel steps.
|
|
||||||
pixelDelta = ticks * pixelPerLine * wheelScrollLines;
|
|
||||||
} else {
|
|
||||||
pixelDelta = wheel.pixelDelta.y;
|
|
||||||
}
|
|
||||||
pixelDelta = Math.round(pixelDelta);
|
|
||||||
if (!pixelDelta)
|
|
||||||
return flickableItem.contentY;
|
|
||||||
|
|
||||||
var minYExtent = flickableItem.originY + flickableItem.topMargin;
|
|
||||||
var maxYExtent = (flickableItem.contentHeight + flickableItem.bottomMargin + flickableItem.originY) - flickableItem.height;
|
|
||||||
//Avoid overscrolling
|
|
||||||
return Math.max(minYExtent, Math.min(maxYExtent, flickableItem.contentY - pixelDelta));
|
|
||||||
}
|
|
||||||
|
|
||||||
propagateComposedEvents: true
|
|
||||||
//Place the mouse area under the flickable
|
|
||||||
z: -1
|
|
||||||
onFlickableChanged: {
|
|
||||||
if (enabled) {
|
|
||||||
flickable.maximumFlickVelocity = 100000;
|
|
||||||
flickable.boundsBehavior = Flickable.StopAtBounds;
|
|
||||||
root.parent = flickable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
acceptedButtons: Qt.NoButton
|
|
||||||
onWheel: {
|
|
||||||
var newPos = calculateNewPosition(flickable, wheel);
|
|
||||||
// Show the scrollbars
|
|
||||||
flickable.flick(0, 0);
|
|
||||||
flickable.contentY = newPos;
|
|
||||||
cancelFlickStateTimer.restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: cancelFlickStateTimer
|
|
||||||
|
|
||||||
//How long the scrollbar will remain visible
|
|
||||||
interval: 500
|
|
||||||
// Hide the scrollbars
|
|
||||||
onTriggered: {
|
|
||||||
flickable.cancelFlick();
|
|
||||||
flickable.movementEnded();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -10,22 +10,32 @@ import QtQuick.Layouts 1.3
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
visible: false
|
|
||||||
enabled: false
|
enabled: false
|
||||||
|
visible: false
|
||||||
|
|
||||||
Dialog {
|
Dialog {
|
||||||
id: showRecoverKeyDialog
|
id: showRecoverKeyDialog
|
||||||
|
|
||||||
property string recoveryKey: ""
|
property string recoveryKey: ""
|
||||||
|
|
||||||
parent: Overlay.overlay
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
height: content.height + implicitFooterHeight + implicitHeaderHeight
|
|
||||||
width: content.width
|
|
||||||
padding: 0
|
|
||||||
modal: true
|
|
||||||
standardButtons: Dialog.Ok
|
|
||||||
closePolicy: Popup.NoAutoClose
|
closePolicy: Popup.NoAutoClose
|
||||||
|
height: content.height + implicitFooterHeight + implicitHeaderHeight
|
||||||
|
modal: true
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
// Workaround palettes not inheriting for popups
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
parent: Overlay.overlay
|
||||||
|
standardButtons: Dialog.Ok
|
||||||
|
width: content.width
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: Nheko.theme.separator
|
||||||
|
border.width: 1
|
||||||
|
color: palette.window
|
||||||
|
radius: Nheko.paddingSmall
|
||||||
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: content
|
id: content
|
||||||
|
@ -33,45 +43,33 @@ Item {
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
Layout.margins: Nheko.paddingMedium
|
Layout.margins: Nheko.paddingMedium
|
||||||
Layout.maximumWidth: (Overlay.overlay ? Overlay.overlay.width : 400) - Nheko.paddingMedium * 4
|
Layout.maximumWidth: (Overlay.overlay ? Overlay.overlay.width : 400) - Nheko.paddingMedium * 4
|
||||||
Layout.fillWidth: true
|
color: palette.text
|
||||||
text: qsTr("This is your recovery key. You will need it to restore access to your encrypted messages and verification keys. Keep this safe. Don't share it with anyone and don't lose it! Do not pass go! Do not collect $200!")
|
text: qsTr("This is your recovery key. You will need it to restore access to your encrypted messages and verification keys. Keep this safe. Don't share it with anyone and don't lose it! Do not pass go! Do not collect $200!")
|
||||||
color: Nheko.colors.text
|
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
TextEdit {
|
TextEdit {
|
||||||
Layout.maximumWidth: (Overlay.overlay ? Overlay.overlay.width : 400) - Nheko.paddingMedium * 4
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.maximumWidth: (Overlay.overlay ? Overlay.overlay.width : 400) - Nheko.paddingMedium * 4
|
||||||
|
color: palette.text
|
||||||
|
font.bold: true
|
||||||
horizontalAlignment: TextEdit.AlignHCenter
|
horizontalAlignment: TextEdit.AlignHCenter
|
||||||
verticalAlignment: TextEdit.AlignVCenter
|
|
||||||
readOnly: true
|
readOnly: true
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
text: showRecoverKeyDialog.recoveryKey
|
text: showRecoverKeyDialog.recoveryKey
|
||||||
color: Nheko.colors.text
|
verticalAlignment: TextEdit.AlignVCenter
|
||||||
font.bold: true
|
|
||||||
wrapMode: TextEdit.Wrap
|
wrapMode: TextEdit.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: Nheko.colors.window
|
|
||||||
border.color: Nheko.theme.separator
|
|
||||||
border.width: 1
|
|
||||||
radius: Nheko.paddingSmall
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
P.MessageDialog {
|
P.MessageDialog {
|
||||||
id: successDialog
|
id: successDialog
|
||||||
|
|
||||||
buttons: P.MessageDialog.Ok
|
buttons: P.MessageDialog.Ok
|
||||||
text: qsTr("Encryption setup successfully")
|
text: qsTr("Encryption setup successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
P.MessageDialog {
|
P.MessageDialog {
|
||||||
id: failureDialog
|
id: failureDialog
|
||||||
|
|
||||||
|
@ -80,85 +78,89 @@ Item {
|
||||||
buttons: P.MessageDialog.Ok
|
buttons: P.MessageDialog.Ok
|
||||||
text: qsTr("Failed to setup encryption: %1").arg(errorMessage)
|
text: qsTr("Failed to setup encryption: %1").arg(errorMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindowDialog {
|
MainWindowDialog {
|
||||||
id: bootstrapCrosssigning
|
id: bootstrapCrosssigning
|
||||||
|
|
||||||
|
// Workaround palettes not inheriting for popups
|
||||||
|
palette: timelineRoot.palette
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: Nheko.theme.separator
|
||||||
|
border.width: 1
|
||||||
|
color: palette.window
|
||||||
|
radius: Nheko.paddingSmall
|
||||||
|
}
|
||||||
|
|
||||||
onAccepted: SelfVerificationStatus.setupCrosssigning(storeSecretsOnline.checked, usePassword.checked ? passwordField.text : "", useOnlineKeyBackup.checked)
|
onAccepted: SelfVerificationStatus.setupCrosssigning(storeSecretsOnline.checked, usePassword.checked ? passwordField.text : "", useOnlineKeyBackup.checked)
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
id: grid
|
id: grid
|
||||||
|
|
||||||
width: bootstrapCrosssigning.useableWidth
|
columnSpacing: 0
|
||||||
columns: 2
|
columns: 2
|
||||||
rowSpacing: 0
|
rowSpacing: 0
|
||||||
columnSpacing: 0
|
width: bootstrapCrosssigning.useableWidth
|
||||||
z: 1
|
z: 1
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
|
color: palette.text
|
||||||
font.pointSize: fontMetrics.font.pointSize * 2
|
font.pointSize: fontMetrics.font.pointSize * 2
|
||||||
text: qsTr("Setup Encryption")
|
text: qsTr("Setup Encryption")
|
||||||
color: Nheko.colors.text
|
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.columnSpan: 2
|
Layout.columnSpan: 2
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
Layout.maximumWidth: grid.width - Nheko.paddingMedium * 2
|
Layout.maximumWidth: grid.width - Nheko.paddingMedium * 2
|
||||||
|
color: palette.text
|
||||||
text: qsTr("Hello and welcome to Matrix!\nIt seems like you are new. Before you can securely encrypt your messages, we need to setup a few small things. You can either press accept immediately or adjust a few basic options. We also try to explain a few of the basics. You can skip those parts, but they might prove to be helpful!")
|
text: qsTr("Hello and welcome to Matrix!\nIt seems like you are new. Before you can securely encrypt your messages, we need to setup a few small things. You can either press accept immediately or adjust a few basic options. We also try to explain a few of the basics. You can skip those parts, but they might prove to be helpful!")
|
||||||
color: Nheko.colors.text
|
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.columnSpan: 1
|
Layout.columnSpan: 1
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
|
Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
|
||||||
|
color: palette.text
|
||||||
text: "Store secrets online.\nYou have a few secrets to make all the encryption magic work. While you can keep them stored only locally, we recommend storing them encrypted on the server. Otherwise it will be painful to recover them. Only disable this if you are paranoid and like losing your data!"
|
text: "Store secrets online.\nYou have a few secrets to make all the encryption magic work. While you can keep them stored only locally, we recommend storing them encrypted on the server. Otherwise it will be painful to recover them. Only disable this if you are paranoid and like losing your data!"
|
||||||
color: Nheko.colors.text
|
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.preferredHeight: storeSecretsOnline.height
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
|
Layout.preferredHeight: storeSecretsOnline.height
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
id: storeSecretsOnline
|
id: storeSecretsOnline
|
||||||
|
|
||||||
checked: true
|
checked: true
|
||||||
|
|
||||||
onClicked: console.log("Store secrets toggled: " + checked)
|
onClicked: console.log("Store secrets toggled: " + checked)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.columnSpan: 1
|
Layout.columnSpan: 1
|
||||||
Layout.rowSpan: 2
|
Layout.margins: Nheko.paddingMedium
|
||||||
Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
|
Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
|
||||||
visible: storeSecretsOnline.checked
|
Layout.rowSpan: 2
|
||||||
|
color: palette.text
|
||||||
text: "Set an online backup password.\nWe recommend you DON'T set a password and instead only rely on the recovery key. You will get a recovery key in any case when storing the cross-signing secrets online, but passwords are usually not very random, so they are easier to attack than a completely random recovery key. If you choose to use a password, DON'T make it the same as your login password, otherwise your server can read all your encrypted messages. (You don't want that.)"
|
text: "Set an online backup password.\nWe recommend you DON'T set a password and instead only rely on the recovery key. You will get a recovery key in any case when storing the cross-signing secrets online, but passwords are usually not very random, so they are easier to attack than a completely random recovery key. If you choose to use a password, DON'T make it the same as your login password, otherwise your server can read all your encrypted messages. (You don't want that.)"
|
||||||
color: Nheko.colors.text
|
visible: storeSecretsOnline.checked
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.topMargin: Nheko.paddingLarge
|
|
||||||
Layout.preferredHeight: storeSecretsOnline.height
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||||
Layout.rowSpan: usePassword.checked ? 1 : 2
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
|
Layout.preferredHeight: storeSecretsOnline.height
|
||||||
|
Layout.rowSpan: usePassword.checked ? 1 : 2
|
||||||
|
Layout.topMargin: Nheko.paddingLarge
|
||||||
visible: storeSecretsOnline.checked
|
visible: storeSecretsOnline.checked
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
|
@ -166,113 +168,108 @@ Item {
|
||||||
|
|
||||||
checked: false
|
checked: false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: passwordField
|
id: passwordField
|
||||||
|
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||||
Layout.columnSpan: 1
|
Layout.columnSpan: 1
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
visible: storeSecretsOnline.checked && usePassword.checked
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.margins: Nheko.paddingMedium
|
Layout.margins: Nheko.paddingMedium
|
||||||
|
Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
visible: storeSecretsOnline.checked && usePassword.checked
|
||||||
|
}
|
||||||
|
Label {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.columnSpan: 1
|
Layout.columnSpan: 1
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
|
Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
|
||||||
|
color: palette.text
|
||||||
text: "Use online key backup.\nStore the keys for your messages securely encrypted online. In general you do want this, because it protects your messages from becoming unreadable, if you log out by accident. It does however carry a small security risk, if you ever share your recovery key by accident. Currently this also has some other weaknesses, that might allow the server to insert new keys into your backup. The server will however never be able to read your messages."
|
text: "Use online key backup.\nStore the keys for your messages securely encrypted online. In general you do want this, because it protects your messages from becoming unreadable, if you log out by accident. It does however carry a small security risk, if you ever share your recovery key by accident. Currently this also has some other weaknesses, that might allow the server to insert new keys into your backup. The server will however never be able to read your messages."
|
||||||
color: Nheko.colors.text
|
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.preferredHeight: storeSecretsOnline.height
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
|
Layout.preferredHeight: storeSecretsOnline.height
|
||||||
|
|
||||||
ToggleButton {
|
ToggleButton {
|
||||||
id: useOnlineKeyBackup
|
id: useOnlineKeyBackup
|
||||||
|
|
||||||
checked: true
|
checked: true
|
||||||
|
|
||||||
onClicked: console.log("Online key backup toggled: " + checked)
|
onClicked: console.log("Online key backup toggled: " + checked)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: Nheko.colors.window
|
|
||||||
border.color: Nheko.theme.separator
|
|
||||||
border.width: 1
|
|
||||||
radius: Nheko.paddingSmall
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindowDialog {
|
MainWindowDialog {
|
||||||
id: verifyMasterKey
|
id: verifyMasterKey
|
||||||
|
|
||||||
|
// Workaround palettes not inheriting for popups
|
||||||
|
palette: timelineRoot.palette
|
||||||
standardButtons: Dialog.Cancel
|
standardButtons: Dialog.Cancel
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
id: masterGrid
|
id: masterGrid
|
||||||
|
|
||||||
width: verifyMasterKey.useableWidth
|
|
||||||
columns: 1
|
columns: 1
|
||||||
|
width: verifyMasterKey.useableWidth
|
||||||
z: 1
|
z: 1
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
|
color: palette.text
|
||||||
//Layout.columnSpan: 2
|
//Layout.columnSpan: 2
|
||||||
font.pointSize: fontMetrics.font.pointSize * 2
|
font.pointSize: fontMetrics.font.pointSize * 2
|
||||||
text: qsTr("Activate Encryption")
|
text: qsTr("Activate Encryption")
|
||||||
color: Nheko.colors.text
|
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.margins: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
Layout.margins: Nheko.paddingMedium
|
||||||
//Layout.columnSpan: 2
|
//Layout.columnSpan: 2
|
||||||
Layout.maximumWidth: grid.width - Nheko.paddingMedium * 2
|
Layout.maximumWidth: grid.width - Nheko.paddingMedium * 2
|
||||||
|
color: palette.text
|
||||||
text: qsTr("It seems like you have encryption already configured for this account. To be able to access your encrypted messages and make this device appear as trusted, you can either verify an existing device or (if you have one) enter your recovery passphrase. Please select one of the options below.\nIf you choose verify, you need to have the other device available. If you choose \"enter passphrase\", you will need your recovery key or passphrase. If you click cancel, you can choose to verify yourself at a later point.")
|
text: qsTr("It seems like you have encryption already configured for this account. To be able to access your encrypted messages and make this device appear as trusted, you can either verify an existing device or (if you have one) enter your recovery passphrase. Please select one of the options below.\nIf you choose verify, you need to have the other device available. If you choose \"enter passphrase\", you will need your recovery key or passphrase. If you click cancel, you can choose to verify yourself at a later point.")
|
||||||
color: Nheko.colors.text
|
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatButton {
|
FlatButton {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("verify")
|
text: qsTr("verify")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
SelfVerificationStatus.verifyMasterKey();
|
SelfVerificationStatus.verifyMasterKey();
|
||||||
verifyMasterKey.close();
|
verifyMasterKey.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatButton {
|
FlatButton {
|
||||||
visible: SelfVerificationStatus.hasSSSS
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("enter passphrase")
|
text: qsTr("enter passphrase")
|
||||||
|
visible: SelfVerificationStatus.hasSSSS
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
SelfVerificationStatus.verifyMasterKeyWithPassphrase();
|
SelfVerificationStatus.verifyMasterKeyWithPassphrase();
|
||||||
verifyMasterKey.close();
|
verifyMasterKey.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
function onSetupCompleted() {
|
||||||
|
successDialog.open();
|
||||||
|
}
|
||||||
|
function onSetupFailed(m) {
|
||||||
|
failureDialog.errorMessage = m;
|
||||||
|
failureDialog.open();
|
||||||
|
}
|
||||||
|
function onShowRecoveryKey(key) {
|
||||||
|
showRecoverKeyDialog.recoveryKey = key;
|
||||||
|
showRecoverKeyDialog.open();
|
||||||
|
}
|
||||||
function onStatusChanged() {
|
function onStatusChanged() {
|
||||||
console.log("STATUS CHANGED: " + SelfVerificationStatus.status);
|
console.log("STATUS CHANGED: " + SelfVerificationStatus.status);
|
||||||
if (SelfVerificationStatus.status == SelfVerificationStatus.NoMasterKey) {
|
if (SelfVerificationStatus.status == SelfVerificationStatus.NoMasterKey) {
|
||||||
|
@ -285,21 +282,6 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onShowRecoveryKey(key) {
|
|
||||||
showRecoverKeyDialog.recoveryKey = key;
|
|
||||||
showRecoverKeyDialog.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSetupCompleted() {
|
|
||||||
successDialog.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSetupFailed(m) {
|
|
||||||
failureDialog.errorMessage = m;
|
|
||||||
failureDialog.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
target: SelfVerificationStatus
|
target: SelfVerificationStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,9 @@ import im.nheko 1.0
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: indicator
|
id: indicator
|
||||||
|
|
||||||
required property int status
|
|
||||||
required property string eventId
|
required property string eventId
|
||||||
|
required property int status
|
||||||
|
|
||||||
width: 16
|
|
||||||
height: 16
|
|
||||||
hoverEnabled: true
|
|
||||||
changeColorOnHover: (status == MtxEvent.Read)
|
|
||||||
cursor: (status == MtxEvent.Read) ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
||||||
ToolTip.visible: hovered && status != MtxEvent.Empty
|
|
||||||
ToolTip.text: {
|
ToolTip.text: {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case MtxEvent.Failed:
|
case MtxEvent.Failed:
|
||||||
|
@ -32,11 +26,11 @@ ImageButton {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onClicked: {
|
ToolTip.visible: hovered && status != MtxEvent.Empty
|
||||||
if (status == MtxEvent.Read)
|
changeColorOnHover: (status == MtxEvent.Read)
|
||||||
room.showReadReceipts(eventId);
|
cursor: (status == MtxEvent.Read) ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
height: 16
|
||||||
}
|
hoverEnabled: true
|
||||||
image: {
|
image: {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case MtxEvent.Failed:
|
case MtxEvent.Failed:
|
||||||
|
@ -51,4 +45,10 @@ ImageButton {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
width: 16
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (status == MtxEvent.Read)
|
||||||
|
room.showReadReceipts(eventId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,72 +13,44 @@ import im.nheko 1.0
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: r
|
id: r
|
||||||
|
|
||||||
required property double proportionalHeight
|
|
||||||
required property int type
|
|
||||||
required property string typeString
|
|
||||||
required property int originalWidth
|
|
||||||
required property string blurhash
|
required property string blurhash
|
||||||
required property string body
|
required property string body
|
||||||
required property string formattedBody
|
required property string callType
|
||||||
|
required property int duration
|
||||||
|
required property int encryptionError
|
||||||
required property string eventId
|
required property string eventId
|
||||||
required property string filename
|
required property string filename
|
||||||
required property string filesize
|
required property string filesize
|
||||||
required property string url
|
required property string formattedBody
|
||||||
required property string thumbnailUrl
|
required property int index
|
||||||
required property bool isOnlyEmoji
|
|
||||||
required property bool isSender
|
|
||||||
required property bool isEncrypted
|
|
||||||
required property bool isEditable
|
required property bool isEditable
|
||||||
required property bool isEdited
|
required property bool isEdited
|
||||||
|
required property bool isEncrypted
|
||||||
|
required property bool isOnlyEmoji
|
||||||
|
required property bool isSender
|
||||||
required property bool isStateEvent
|
required property bool isStateEvent
|
||||||
|
required property int notificationlevel
|
||||||
|
required property int originalWidth
|
||||||
|
required property double proportionalHeight
|
||||||
|
required property var reactions
|
||||||
|
required property int relatedEventCacheBuster
|
||||||
required property string replyTo
|
required property string replyTo
|
||||||
|
required property string roomName
|
||||||
|
required property string roomTopic
|
||||||
|
required property int status
|
||||||
required property string threadId
|
required property string threadId
|
||||||
|
required property string thumbnailUrl
|
||||||
|
required property var timestamp
|
||||||
|
required property int trustlevel
|
||||||
|
required property int type
|
||||||
|
required property string typeString
|
||||||
|
required property string url
|
||||||
required property string userId
|
required property string userId
|
||||||
required property string userName
|
required property string userName
|
||||||
required property string roomTopic
|
|
||||||
required property string roomName
|
|
||||||
required property string callType
|
|
||||||
required property var reactions
|
|
||||||
required property int trustlevel
|
|
||||||
required property int notificationlevel
|
|
||||||
required property int encryptionError
|
|
||||||
required property int duration
|
|
||||||
required property var timestamp
|
|
||||||
required property int status
|
|
||||||
required property int index
|
|
||||||
required property int relatedEventCacheBuster
|
|
||||||
|
|
||||||
|
height: row.height + (reactionRow.height > 0 ? reactionRow.height - 2 : 0) + unreadRow.height
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
height: row.height+(reactionRow.height > 0 ? reactionRow.height-2 : 0 )+unreadRow.height
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
color: (Settings.messageHoverHighlight && hovered) ? Nheko.colors.alternateBase : "transparent"
|
|
||||||
anchors.fill: parent
|
|
||||||
// this looks better without margins
|
|
||||||
TapHandler {
|
|
||||||
acceptedButtons: Qt.RightButton
|
|
||||||
onSingleTapped: messageContextMenu.show(eventId, threadId, type, isSender, isEncrypted, isEditable, contentItem.child.hoveredLink, contentItem.child.copyText)
|
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
|
||||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
onPressAndHold: messageContextMenu.show(eventId, threadId, type, isSender, isEncrypted, isEditable, contentItem.child.hoveredLink, contentItem.child.copyText)
|
|
||||||
onDoubleClicked: room.reply = eventId
|
|
||||||
|
|
||||||
DragHandler {
|
|
||||||
id: draghandler
|
|
||||||
yAxis.enabled: false
|
|
||||||
xAxis.maximum: 100
|
|
||||||
xAxis.minimum: -100
|
|
||||||
onActiveChanged: {
|
|
||||||
if(!active && (x < -70 || x > 70))
|
|
||||||
room.reply = eventId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
states: State {
|
states: State {
|
||||||
name: "dragging"
|
name: "dragging"
|
||||||
when: draghandler.active
|
when: draghandler.active
|
||||||
|
@ -86,265 +58,292 @@ AbstractButton {
|
||||||
transitions: Transition {
|
transitions: Transition {
|
||||||
from: "dragging"
|
from: "dragging"
|
||||||
to: ""
|
to: ""
|
||||||
|
|
||||||
PropertyAnimation {
|
PropertyAnimation {
|
||||||
target: r
|
|
||||||
properties: "x"
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
to: 0
|
|
||||||
duration: 100
|
duration: 100
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
properties: "x"
|
||||||
|
target: r
|
||||||
|
to: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
let link = contentItem.child.linkAt != undefined && contentItem.child.linkAt(pressX-row.x-msg.x, pressY-row.y-msg.y-contentItem.y);
|
let link = contentItem.child.linkAt != undefined && contentItem.child.linkAt(pressX - row.x - msg.x, pressY - row.y - msg.y - contentItem.y);
|
||||||
if (link) {
|
if (link) {
|
||||||
Nheko.openLink(link)
|
Nheko.openLink(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onDoubleClicked: room.reply = eventId
|
||||||
|
onPressAndHold: messageContextMenu.show(eventId, threadId, type, isSender, isEncrypted, isEditable, contentItem.child.hoveredLink, contentItem.child.copyText)
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: (Settings.messageHoverHighlight && hovered) ? palette.alternateBase : "transparent"
|
||||||
|
|
||||||
|
// this looks better without margins
|
||||||
|
TapHandler {
|
||||||
|
acceptedButtons: Qt.RightButton
|
||||||
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
|
||||||
|
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||||
|
|
||||||
|
onSingleTapped: messageContextMenu.show(eventId, threadId, type, isSender, isEncrypted, isEditable, contentItem.child.hoveredLink, contentItem.child.copyText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DragHandler {
|
||||||
|
id: draghandler
|
||||||
|
|
||||||
|
xAxis.maximum: 100
|
||||||
|
xAxis.minimum: -100
|
||||||
|
yAxis.enabled: false
|
||||||
|
|
||||||
|
onActiveChanged: {
|
||||||
|
if (!active && (x < -70 || x > 70))
|
||||||
|
room.reply = eventId;
|
||||||
|
}
|
||||||
|
}
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
anchors.leftMargin: Settings.smallAvatars? 0 : (Nheko.avatarSize + 8) // align bubble with section header
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
|
ToolTip.text: qsTr("Part of a thread")
|
||||||
|
ToolTip.visible: hovered
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Settings.smallAvatars ? 0 : (Nheko.avatarSize + 8) // align bubble with section header
|
||||||
|
height: parent.height
|
||||||
visible: threadId
|
visible: threadId
|
||||||
width: 4
|
width: 4
|
||||||
height: parent.height
|
|
||||||
|
onClicked: room.thread = threadId
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: threadLine
|
id: threadLine
|
||||||
|
|
||||||
color: TimelineManager.userColor(threadId, Nheko.colors.base)
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
color: TimelineManager.userColor(threadId, palette.base)
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
|
||||||
ToolTip.text: qsTr("Part of a thread")
|
|
||||||
onClicked: room.thread = threadId
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: row
|
id: row
|
||||||
property bool bubbleOnRight : isSender && Settings.bubbles
|
|
||||||
anchors.leftMargin: (isStateEvent || Settings.smallAvatars? 0 : (Nheko.avatarSize + 8)) + (threadId ? 6 : 0) // align bubble with section header
|
|
||||||
anchors.left: (isStateEvent || bubbleOnRight) ? undefined : parent.left
|
|
||||||
anchors.right: (isStateEvent || !bubbleOnRight) ? undefined : parent.right
|
|
||||||
anchors.horizontalCenter: isStateEvent? parent.horizontalCenter : undefined
|
|
||||||
property int maxWidth: (parent.width-(Settings.smallAvatars || isStateEvent? 0 : Nheko.avatarSize+8))*(Settings.bubbles && !isStateEvent? 0.9 : 1)
|
|
||||||
width: Settings.bubbles? Math.min(maxWidth,Math.max(reply.implicitWidth+8,contentItem.implicitWidth+metadata.width+20)) : maxWidth
|
|
||||||
height: msg.height+msg.anchors.margins*2
|
|
||||||
|
|
||||||
property color userColor: TimelineManager.userColor(userId, Nheko.colors.base)
|
property color bgColor: palette.base
|
||||||
property color bgColor: Nheko.colors.base
|
property bool bubbleOnRight: isSender && Settings.bubbles
|
||||||
color: (Settings.bubbles && !isStateEvent) ? Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.2)) : "#00000000"
|
property int maxWidth: (parent.width - (Settings.smallAvatars || isStateEvent ? 0 : Nheko.avatarSize + 8)) * (Settings.bubbles && !isStateEvent ? 0.9 : 1)
|
||||||
radius: 4
|
property color userColor: TimelineManager.userColor(userId, palette.base)
|
||||||
border.width: r.notificationlevel == MtxEvent.Highlight ? 1 : 0
|
|
||||||
|
anchors.horizontalCenter: isStateEvent ? parent.horizontalCenter : undefined
|
||||||
|
anchors.left: (isStateEvent || bubbleOnRight) ? undefined : parent.left
|
||||||
|
anchors.leftMargin: (isStateEvent || Settings.smallAvatars ? 0 : (Nheko.avatarSize + 8)) + (threadId ? 6 : 0) // align bubble with section header
|
||||||
|
anchors.right: (isStateEvent || !bubbleOnRight) ? undefined : parent.right
|
||||||
border.color: Nheko.theme.red
|
border.color: Nheko.theme.red
|
||||||
|
border.width: r.notificationlevel == MtxEvent.Highlight ? 1 : 0
|
||||||
|
color: (Settings.bubbles && !isStateEvent) ? Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.2)) : "#00000000"
|
||||||
|
height: msg.height + msg.anchors.margins * 2
|
||||||
|
radius: 4
|
||||||
|
width: Settings.bubbles ? Math.min(maxWidth, Math.max(reply.implicitWidth + 8, contentItem.implicitWidth + metadata.width + 20)) : maxWidth
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
|
id: msg
|
||||||
|
|
||||||
|
columnSpacing: 2
|
||||||
|
columns: Settings.bubbles ? 1 : 2
|
||||||
|
rowSpacing: 0
|
||||||
|
rows: Settings.bubbles ? 3 : 2
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
left: parent.left
|
left: parent.left
|
||||||
top: parent.top
|
|
||||||
right: parent.right
|
|
||||||
margins: (Settings.bubbles && ! isStateEvent)? 4 : 2
|
|
||||||
leftMargin: 4
|
leftMargin: 4
|
||||||
|
margins: (Settings.bubbles && !isStateEvent) ? 4 : 2
|
||||||
|
right: parent.right
|
||||||
rightMargin: 4
|
rightMargin: 4
|
||||||
|
top: parent.top
|
||||||
}
|
}
|
||||||
id: msg
|
|
||||||
rowSpacing: 0
|
|
||||||
columnSpacing: 2
|
|
||||||
columns: Settings.bubbles? 1 : 2
|
|
||||||
rows: Settings.bubbles? 3 : 2
|
|
||||||
|
|
||||||
// fancy reply, if this is a reply
|
// fancy reply, if this is a reply
|
||||||
Reply {
|
Reply {
|
||||||
Layout.row: 0
|
|
||||||
Layout.column: 0
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.maximumWidth: Settings.bubbles? Number.MAX_VALUE : implicitWidth
|
|
||||||
Layout.bottomMargin: visible? 2 : 0
|
|
||||||
Layout.preferredHeight: height
|
|
||||||
id: reply
|
id: reply
|
||||||
|
|
||||||
function fromModel(role) {
|
function fromModel(role) {
|
||||||
return replyTo != "" ? room.dataById(replyTo, role, r.eventId) : null;
|
return replyTo != "" ? room.dataById(replyTo, role, r.eventId) : null;
|
||||||
}
|
}
|
||||||
visible: replyTo
|
|
||||||
userColor: r.relatedEventCacheBuster, TimelineManager.userColor(userId, Nheko.colors.base)
|
Layout.bottomMargin: visible ? 2 : 0
|
||||||
|
Layout.column: 0
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: Settings.bubbles ? Number.MAX_VALUE : implicitWidth
|
||||||
|
Layout.preferredHeight: height
|
||||||
|
Layout.row: 0
|
||||||
blurhash: r.relatedEventCacheBuster, fromModel(Room.Blurhash) ?? ""
|
blurhash: r.relatedEventCacheBuster, fromModel(Room.Blurhash) ?? ""
|
||||||
body: r.relatedEventCacheBuster, fromModel(Room.Body) ?? ""
|
body: r.relatedEventCacheBuster, fromModel(Room.Body) ?? ""
|
||||||
formattedBody: r.relatedEventCacheBuster, fromModel(Room.FormattedBody) ?? ""
|
callType: r.relatedEventCacheBuster, fromModel(Room.Voip) ?? ""
|
||||||
|
duration: r.relatedEventCacheBuster, fromModel(Room.Duration) ?? 0
|
||||||
|
encryptionError: r.relatedEventCacheBuster, fromModel(Room.EncryptionError) ?? 0
|
||||||
eventId: fromModel(Room.EventId) ?? ""
|
eventId: fromModel(Room.EventId) ?? ""
|
||||||
filename: r.relatedEventCacheBuster, fromModel(Room.Filename) ?? ""
|
filename: r.relatedEventCacheBuster, fromModel(Room.Filename) ?? ""
|
||||||
filesize: r.relatedEventCacheBuster, fromModel(Room.Filesize) ?? ""
|
filesize: r.relatedEventCacheBuster, fromModel(Room.Filesize) ?? ""
|
||||||
|
formattedBody: r.relatedEventCacheBuster, fromModel(Room.FormattedBody) ?? ""
|
||||||
|
isOnlyEmoji: r.relatedEventCacheBuster, fromModel(Room.IsOnlyEmoji) ?? false
|
||||||
|
isStateEvent: r.relatedEventCacheBuster, fromModel(Room.IsStateEvent) ?? false
|
||||||
|
originalWidth: r.relatedEventCacheBuster, fromModel(Room.OriginalWidth) ?? 0
|
||||||
proportionalHeight: r.relatedEventCacheBuster, fromModel(Room.ProportionalHeight) ?? 1
|
proportionalHeight: r.relatedEventCacheBuster, fromModel(Room.ProportionalHeight) ?? 1
|
||||||
|
relatedEventCacheBuster: r.relatedEventCacheBuster, fromModel(Room.RelatedEventCacheBuster) ?? 0
|
||||||
|
roomName: r.relatedEventCacheBuster, fromModel(Room.RoomName) ?? ""
|
||||||
|
roomTopic: r.relatedEventCacheBuster, fromModel(Room.RoomTopic) ?? ""
|
||||||
|
thumbnailUrl: r.relatedEventCacheBuster, fromModel(Room.ThumbnailUrl) ?? ""
|
||||||
type: r.relatedEventCacheBuster, fromModel(Room.Type) ?? MtxEvent.UnknownMessage
|
type: r.relatedEventCacheBuster, fromModel(Room.Type) ?? MtxEvent.UnknownMessage
|
||||||
typeString: r.relatedEventCacheBuster, fromModel(Room.TypeString) ?? ""
|
typeString: r.relatedEventCacheBuster, fromModel(Room.TypeString) ?? ""
|
||||||
url: r.relatedEventCacheBuster, fromModel(Room.Url) ?? ""
|
url: r.relatedEventCacheBuster, fromModel(Room.Url) ?? ""
|
||||||
originalWidth: r.relatedEventCacheBuster, fromModel(Room.OriginalWidth) ?? 0
|
userColor: r.relatedEventCacheBuster, TimelineManager.userColor(userId, palette.base)
|
||||||
isOnlyEmoji: r.relatedEventCacheBuster, fromModel(Room.IsOnlyEmoji) ?? false
|
|
||||||
isStateEvent: r.relatedEventCacheBuster, fromModel(Room.IsStateEvent) ?? false
|
|
||||||
userId: r.relatedEventCacheBuster, fromModel(Room.UserId) ?? ""
|
userId: r.relatedEventCacheBuster, fromModel(Room.UserId) ?? ""
|
||||||
userName: r.relatedEventCacheBuster, fromModel(Room.UserName) ?? ""
|
userName: r.relatedEventCacheBuster, fromModel(Room.UserName) ?? ""
|
||||||
thumbnailUrl: r.relatedEventCacheBuster, fromModel(Room.ThumbnailUrl) ?? ""
|
visible: replyTo
|
||||||
duration: r.relatedEventCacheBuster, fromModel(Room.Duration) ?? ""
|
|
||||||
roomTopic: r.relatedEventCacheBuster, fromModel(Room.RoomTopic) ?? ""
|
|
||||||
roomName: r.relatedEventCacheBuster, fromModel(Room.RoomName) ?? ""
|
|
||||||
callType: r.relatedEventCacheBuster, fromModel(Room.CallType) ?? ""
|
|
||||||
encryptionError: r.relatedEventCacheBuster, fromModel(Room.EncryptionError) ?? ""
|
|
||||||
relatedEventCacheBuster: r.relatedEventCacheBuster, fromModel(Room.RelatedEventCacheBuster) ?? 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// actual message content
|
// actual message content
|
||||||
MessageDelegate {
|
MessageDelegate {
|
||||||
Layout.row: 1
|
id: contentItem
|
||||||
|
|
||||||
Layout.column: 0
|
Layout.column: 0
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: height
|
Layout.preferredHeight: height
|
||||||
id: contentItem
|
Layout.row: 1
|
||||||
|
|
||||||
blurhash: r.blurhash
|
blurhash: r.blurhash
|
||||||
body: r.body
|
body: r.body
|
||||||
formattedBody: r.formattedBody
|
callType: r.callType
|
||||||
|
duration: r.duration
|
||||||
|
encryptionError: r.encryptionError
|
||||||
eventId: r.eventId
|
eventId: r.eventId
|
||||||
filename: r.filename
|
filename: r.filename
|
||||||
filesize: r.filesize
|
filesize: r.filesize
|
||||||
|
formattedBody: r.formattedBody
|
||||||
|
isOnlyEmoji: r.isOnlyEmoji
|
||||||
|
isReply: false
|
||||||
|
isStateEvent: r.isStateEvent
|
||||||
|
metadataWidth: metadata.width
|
||||||
|
originalWidth: r.originalWidth
|
||||||
proportionalHeight: r.proportionalHeight
|
proportionalHeight: r.proportionalHeight
|
||||||
|
relatedEventCacheBuster: r.relatedEventCacheBuster
|
||||||
|
roomName: r.roomName
|
||||||
|
roomTopic: r.roomTopic
|
||||||
|
thumbnailUrl: r.thumbnailUrl
|
||||||
type: r.type
|
type: r.type
|
||||||
typeString: r.typeString ?? ""
|
typeString: r.typeString ?? ""
|
||||||
url: r.url
|
url: r.url
|
||||||
thumbnailUrl: r.thumbnailUrl
|
|
||||||
duration: r.duration
|
|
||||||
originalWidth: r.originalWidth
|
|
||||||
isOnlyEmoji: r.isOnlyEmoji
|
|
||||||
isStateEvent: r.isStateEvent
|
|
||||||
userId: r.userId
|
userId: r.userId
|
||||||
userName: r.userName
|
userName: r.userName
|
||||||
roomTopic: r.roomTopic
|
|
||||||
roomName: r.roomName
|
|
||||||
callType: r.callType
|
|
||||||
encryptionError: r.encryptionError
|
|
||||||
relatedEventCacheBuster: r.relatedEventCacheBuster
|
|
||||||
isReply: false
|
|
||||||
metadataWidth: metadata.width
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: metadata
|
id: metadata
|
||||||
Layout.column: Settings.bubbles? 0 : 1
|
|
||||||
Layout.row: Settings.bubbles? 2 : 0
|
property int iconSize: Math.floor(fontMetrics.ascent * scaling)
|
||||||
Layout.rowSpan: Settings.bubbles? 1 : 2
|
property double scaling: Settings.bubbles ? 0.75 : 1
|
||||||
Layout.bottomMargin: -2
|
|
||||||
Layout.topMargin: (contentItem.fitsMetadata && Settings.bubbles)? -height-Layout.bottomMargin : 0
|
|
||||||
Layout.alignment: Qt.AlignTop | Qt.AlignRight
|
Layout.alignment: Qt.AlignTop | Qt.AlignRight
|
||||||
|
Layout.bottomMargin: -2
|
||||||
|
Layout.column: Settings.bubbles ? 0 : 1
|
||||||
Layout.preferredWidth: implicitWidth
|
Layout.preferredWidth: implicitWidth
|
||||||
visible: !isStateEvent
|
Layout.row: Settings.bubbles ? 2 : 0
|
||||||
|
Layout.rowSpan: Settings.bubbles ? 1 : 2
|
||||||
|
Layout.topMargin: (contentItem.fitsMetadata && Settings.bubbles) ? -height - Layout.bottomMargin : 0
|
||||||
spacing: 2
|
spacing: 2
|
||||||
|
visible: !isStateEvent
|
||||||
property double scaling: Settings.bubbles? 0.75 : 1
|
|
||||||
|
|
||||||
property int iconSize: Math.floor(fontMetrics.ascent*scaling)
|
|
||||||
|
|
||||||
StatusIndicator {
|
StatusIndicator {
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
height: parent.iconSize
|
|
||||||
width: parent.iconSize
|
|
||||||
status: r.status
|
|
||||||
eventId: r.eventId
|
|
||||||
anchors.verticalCenter: ts.verticalCenter
|
anchors.verticalCenter: ts.verticalCenter
|
||||||
}
|
eventId: r.eventId
|
||||||
|
|
||||||
Image {
|
|
||||||
visible: isEdited || eventId == room.edit
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
|
||||||
height: parent.iconSize
|
height: parent.iconSize
|
||||||
|
status: r.status
|
||||||
width: parent.iconSize
|
width: parent.iconSize
|
||||||
sourceSize.width: parent.iconSize * Screen.devicePixelRatio
|
}
|
||||||
sourceSize.height: parent.iconSize * Screen.devicePixelRatio
|
Image {
|
||||||
source: "image://colorimage/:/icons/icons/ui/edit.svg?" + ((eventId == room.edit) ? Nheko.colors.highlight : Nheko.colors.buttonText)
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
ToolTip.visible: editHovered.hovered
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("Edited")
|
ToolTip.text: qsTr("Edited")
|
||||||
|
ToolTip.visible: editHovered.hovered
|
||||||
anchors.verticalCenter: ts.verticalCenter
|
anchors.verticalCenter: ts.verticalCenter
|
||||||
|
height: parent.iconSize
|
||||||
|
source: "image://colorimage/:/icons/icons/ui/edit.svg?" + ((eventId == room.edit) ? palette.highlight : palette.buttonText)
|
||||||
|
sourceSize.height: parent.iconSize * Screen.devicePixelRatio
|
||||||
|
sourceSize.width: parent.iconSize * Screen.devicePixelRatio
|
||||||
|
visible: isEdited || eventId == room.edit
|
||||||
|
width: parent.iconSize
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: editHovered
|
id: editHovered
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
visible: threadId
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
height: parent.iconSize
|
|
||||||
width: parent.iconSize
|
|
||||||
image: ":/icons/icons/ui/thread.svg"
|
|
||||||
buttonTextColor: TimelineManager.userColor(threadId, Nheko.colors.base)
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: qsTr("Part of a thread")
|
ToolTip.text: qsTr("Part of a thread")
|
||||||
|
ToolTip.visible: hovered
|
||||||
anchors.verticalCenter: ts.verticalCenter
|
anchors.verticalCenter: ts.verticalCenter
|
||||||
|
buttonTextColor: TimelineManager.userColor(threadId, palette.base)
|
||||||
|
height: parent.iconSize
|
||||||
|
image: ":/icons/icons/ui/thread.svg"
|
||||||
|
visible: threadId
|
||||||
|
width: parent.iconSize
|
||||||
|
|
||||||
onClicked: room.thread = threadId
|
onClicked: room.thread = threadId
|
||||||
}
|
}
|
||||||
|
|
||||||
EncryptionIndicator {
|
EncryptionIndicator {
|
||||||
visible: room.isEncrypted
|
|
||||||
encrypted: isEncrypted
|
|
||||||
trust: trustlevel
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
height: parent.iconSize
|
|
||||||
width: parent.iconSize
|
|
||||||
sourceSize.width: parent.iconSize * Screen.devicePixelRatio
|
|
||||||
sourceSize.height: parent.iconSize * Screen.devicePixelRatio
|
|
||||||
anchors.verticalCenter: ts.verticalCenter
|
anchors.verticalCenter: ts.verticalCenter
|
||||||
|
encrypted: isEncrypted
|
||||||
|
height: parent.iconSize
|
||||||
|
sourceSize.height: parent.iconSize * Screen.devicePixelRatio
|
||||||
|
sourceSize.width: parent.iconSize * Screen.devicePixelRatio
|
||||||
|
trust: trustlevel
|
||||||
|
visible: room.isEncrypted
|
||||||
|
width: parent.iconSize
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: ts
|
id: ts
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
Layout.preferredWidth: implicitWidth
|
Layout.preferredWidth: implicitWidth
|
||||||
text: timestamp.toLocaleTimeString(Locale.ShortFormat)
|
|
||||||
color: Nheko.inactiveColors.text
|
|
||||||
ToolTip.visible: ma.hovered
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: Qt.formatDateTime(timestamp, Qt.DefaultLocaleLongDate)
|
ToolTip.text: Qt.formatDateTime(timestamp, Qt.DefaultLocaleLongDate)
|
||||||
font.pointSize: fontMetrics.font.pointSize*parent.scaling
|
ToolTip.visible: ma.hovered
|
||||||
|
color: palette.inactive.text
|
||||||
|
font.pointSize: fontMetrics.font.pointSize * parent.scaling
|
||||||
|
text: timestamp.toLocaleTimeString(Locale.ShortFormat)
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: ma
|
id: ma
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Reactions {
|
Reactions {
|
||||||
anchors {
|
|
||||||
top: row.bottom
|
|
||||||
topMargin: -4
|
|
||||||
left: row.bubbleOnRight? undefined : row.left
|
|
||||||
right: row.bubbleOnRight? row.right : undefined
|
|
||||||
}
|
|
||||||
width: row.maxWidth
|
|
||||||
layoutDirection: row.bubbleOnRight? Qt.RightToLeft : Qt.LeftToRight
|
|
||||||
|
|
||||||
id: reactionRow
|
id: reactionRow
|
||||||
|
|
||||||
reactions: r.reactions
|
|
||||||
eventId: r.eventId
|
eventId: r.eventId
|
||||||
}
|
layoutDirection: row.bubbleOnRight ? Qt.RightToLeft : Qt.LeftToRight
|
||||||
|
reactions: r.reactions
|
||||||
|
width: row.maxWidth
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: row.bubbleOnRight ? undefined : row.left
|
||||||
|
right: row.bubbleOnRight ? row.right : undefined
|
||||||
|
top: row.bottom
|
||||||
|
topMargin: -4
|
||||||
|
}
|
||||||
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: unreadRow
|
id: unreadRow
|
||||||
|
|
||||||
|
color: palette.highlight
|
||||||
|
height: visible ? 3 : 0
|
||||||
|
visible: (r.index > 0 && (room.fullyReadEventId == r.eventId))
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
top: reactionRow.bottom
|
|
||||||
topMargin: 5
|
|
||||||
left: parent.left
|
left: parent.left
|
||||||
right: parent.right
|
right: parent.right
|
||||||
|
top: reactionRow.bottom
|
||||||
|
topMargin: 5
|
||||||
}
|
}
|
||||||
color: Nheko.colors.highlight
|
|
||||||
|
|
||||||
visible: (r.index > 0 && (room.fullyReadEventId == r.eventId))
|
|
||||||
height: visible ? 3 : 0
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,101 +8,97 @@ import "./device-verification"
|
||||||
import "./emoji"
|
import "./emoji"
|
||||||
import "./ui"
|
import "./ui"
|
||||||
import "./voip"
|
import "./voip"
|
||||||
import Qt.labs.platform 1.1 as Platform
|
import Qt.labs.platform as Platform
|
||||||
import QtQuick 2.15
|
import QtQuick
|
||||||
import QtQuick.Controls 2.5
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts
|
||||||
import QtQuick.Particles 2.15
|
import QtQuick.Particles
|
||||||
import QtQuick.Window 2.13
|
import QtQuick.Window
|
||||||
import im.nheko 1.0
|
import im.nheko
|
||||||
import im.nheko.EmojiModel 1.0
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: timelineView
|
id: timelineView
|
||||||
|
|
||||||
|
required property PrivacyScreen privacyScreen
|
||||||
property var room: null
|
property var room: null
|
||||||
property var roomPreview: null
|
property var roomPreview: null
|
||||||
property bool showBackButton: false
|
|
||||||
property bool shouldEffectsRun: false
|
property bool shouldEffectsRun: false
|
||||||
required property PrivacyScreen privacyScreen
|
property bool showBackButton: false
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
onRoomChanged: if (room != null) room.triggerSpecialEffects()
|
|
||||||
|
|
||||||
StickerPicker {
|
|
||||||
id: emojiPopup
|
|
||||||
|
|
||||||
colors: Nheko.colors
|
|
||||||
emoji: true
|
|
||||||
}
|
|
||||||
|
|
||||||
// focus message input on key press, but not on Ctrl-C and such.
|
// focus message input on key press, but not on Ctrl-C and such.
|
||||||
Keys.onPressed: {
|
Keys.onPressed: event => {
|
||||||
if (event.text && event.key !== Qt.Key_Enter && event.key !== Qt.Key_Return && !topBar.searchHasFocus) {
|
if (event.text && event.key !== Qt.Key_Enter && event.key !== Qt.Key_Return && !topBar.searchHasFocus) {
|
||||||
TimelineManager.focusMessageInput();
|
TimelineManager.focusMessageInput();
|
||||||
room.input.setText(room.input.text + event.text);
|
room.input.setText(room.input.text + event.text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onRoomChanged: if (room != null)
|
||||||
|
room.triggerSpecialEffects()
|
||||||
|
|
||||||
|
StickerPicker {
|
||||||
|
id: emojiPopup
|
||||||
|
|
||||||
|
emoji: true
|
||||||
|
}
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Close
|
sequence: StandardKey.Close
|
||||||
|
|
||||||
onActivated: Rooms.resetCurrentRoom()
|
onActivated: Rooms.resetCurrentRoom()
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
visible: !room && !TimelineManager.isInitialSync && (!roomPreview || !roomPreview.roomid)
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: qsTr("No room open")
|
|
||||||
font.pointSize: 24
|
font.pointSize: 24
|
||||||
color: Nheko.colors.text
|
text: qsTr("No room open")
|
||||||
|
visible: !room && !TimelineManager.isInitialSync && (!roomPreview || !roomPreview.roomid)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
visible: TimelineManager.isInitialSync
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
foreground: Nheko.colors.mid
|
foreground: palette.mid
|
||||||
running: TimelineManager.isInitialSync
|
|
||||||
// height is somewhat arbitrary here... don't set width because width scales w/ height
|
// height is somewhat arbitrary here... don't set width because width scales w/ height
|
||||||
height: parent.height / 16
|
height: parent.height / 16
|
||||||
z: 3
|
|
||||||
opacity: hh.hovered ? 0.3 : 1
|
opacity: hh.hovered ? 0.3 : 1
|
||||||
|
running: TimelineManager.isInitialSync
|
||||||
|
visible: TimelineManager.isInitialSync
|
||||||
|
z: 3
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation { duration: 100; }
|
NumberAnimation {
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: hh
|
id: hh
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: timelineLayout
|
id: timelineLayout
|
||||||
|
|
||||||
visible: room != null && !room.isSpace
|
|
||||||
enabled: visible
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
enabled: visible
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
visible: room != null && !room.isSpace
|
||||||
|
|
||||||
TopBar {
|
TopBar {
|
||||||
id: topBar
|
id: topBar
|
||||||
|
|
||||||
showBackButton: timelineView.showBackButton
|
showBackButton: timelineView.showBackButton
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
color: Nheko.theme.separator
|
||||||
height: 1
|
height: 1
|
||||||
z: 3
|
z: 3
|
||||||
color: Nheko.theme.separator
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: msgView
|
id: msgView
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
color: Nheko.colors.base
|
Layout.fillWidth: true
|
||||||
|
color: palette.base
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -120,143 +116,121 @@ Item {
|
||||||
|
|
||||||
target: timelineView
|
target: timelineView
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageView {
|
MessageView {
|
||||||
|
Layout.fillWidth: true
|
||||||
implicitHeight: msgView.height - typingIndicator.height
|
implicitHeight: msgView.height - typingIndicator.height
|
||||||
searchString: topBar.searchString
|
searchString: topBar.searchString
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
source: CallManager.isOnCall && CallManager.callType != CallType.VOICE ? "voip/VideoCall.qml" : ""
|
source: CallManager.isOnCall && CallManager.callType != Voip.VOICE ? "voip/VideoCall.qml" : ""
|
||||||
|
|
||||||
onLoaded: TimelineManager.setVideoCallItem()
|
onLoaded: TimelineManager.setVideoCallItem()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TypingIndicator {
|
TypingIndicator {
|
||||||
id: typingIndicator
|
id: typingIndicator
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CallInviteBar {
|
CallInviteBar {
|
||||||
id: callInviteBar
|
id: callInviteBar
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
z: 3
|
z: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
ActiveCallBar {
|
ActiveCallBar {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
z: 3
|
z: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
z: 3
|
|
||||||
height: 1
|
|
||||||
color: Nheko.theme.separator
|
color: Nheko.theme.separator
|
||||||
|
height: 1
|
||||||
|
z: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
UploadBox {
|
UploadBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageInputWarning {
|
MessageInputWarning {
|
||||||
text: qsTr("You are about to notify the whole room")
|
text: qsTr("You are about to notify the whole room")
|
||||||
visible: (room && room.permissions.canPingRoom() && room.input.containsAtRoom)
|
visible: (room && room.permissions.canPingRoom() && room.input.containsAtRoom)
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageInputWarning {
|
MessageInputWarning {
|
||||||
text: qsTr("The command /%1 is not recognized and will be sent as part of your message").arg(room ? room.input.currentCommand : "")
|
text: qsTr("The command /%1 is not recognized and will be sent as part of your message").arg(room ? room.input.currentCommand : "")
|
||||||
visible: room ? room.input.containsInvalidCommand && !room.input.containsIncompleteCommand : false
|
visible: room ? room.input.containsInvalidCommand && !room.input.containsIncompleteCommand : false
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageInputWarning {
|
MessageInputWarning {
|
||||||
|
bubbleColor: Nheko.theme.orange
|
||||||
text: qsTr("/%1 looks like an incomplete command. To send it anyway, add a space to the end of your message.").arg(room ? room.input.currentCommand : "")
|
text: qsTr("/%1 looks like an incomplete command. To send it anyway, add a space to the end of your message.").arg(room ? room.input.currentCommand : "")
|
||||||
visible: room ? room.input.containsIncompleteCommand : false
|
visible: room ? room.input.containsIncompleteCommand : false
|
||||||
bubbleColor: Nheko.theme.orange
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReplyPopup {
|
ReplyPopup {
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageInput {
|
MessageInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: preview
|
id: preview
|
||||||
|
|
||||||
|
property string avatarUrl: room ? room.roomAvatarUrl : (roomPreview ? roomPreview.roomAvatarUrl : "")
|
||||||
|
property string reason: roomPreview ? roomPreview.reason : ""
|
||||||
property string roomId: room ? room.roomId : (roomPreview ? roomPreview.roomid : "")
|
property string roomId: room ? room.roomId : (roomPreview ? roomPreview.roomid : "")
|
||||||
property string roomName: room ? room.roomName : (roomPreview ? roomPreview.roomName : "")
|
property string roomName: room ? room.roomName : (roomPreview ? roomPreview.roomName : "")
|
||||||
property string roomTopic: room ? room.roomTopic : (roomPreview ? roomPreview.roomTopic : "")
|
property string roomTopic: room ? room.roomTopic : (roomPreview ? roomPreview.roomTopic : "")
|
||||||
property string avatarUrl: room ? room.roomAvatarUrl : (roomPreview ? roomPreview.roomAvatarUrl : "")
|
|
||||||
property string reason: roomPreview ? roomPreview.reason : ""
|
|
||||||
|
|
||||||
visible: room != null && room.isSpace || roomPreview != null
|
|
||||||
enabled: visible
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Nheko.paddingLarge
|
anchors.margins: Nheko.paddingLarge
|
||||||
|
enabled: visible
|
||||||
spacing: Nheko.paddingLarge
|
spacing: Nheko.paddingLarge
|
||||||
|
visible: room != null && room.isSpace || roomPreview != null
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
url: parent.avatarUrl.replace("mxc://", "image://MxcImage/")
|
Layout.alignment: Qt.AlignHCenter
|
||||||
roomid: parent.roomId
|
|
||||||
displayName: parent.roomName
|
displayName: parent.roomName
|
||||||
height: 130
|
|
||||||
width: 130
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
enabled: false
|
enabled: false
|
||||||
|
height: 130
|
||||||
|
roomid: parent.roomId
|
||||||
|
url: parent.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
width: 130
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: !roomPreview.isFetched ? qsTr("No preview available") : preview.roomName
|
|
||||||
font.pixelSize: 24
|
font.pixelSize: 24
|
||||||
|
text: !(roomPreview?.isFetched ?? false) ? qsTr("No preview available") : preview.roomName
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
|
ToolTip.text: qsTr("Settings")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/settings.svg"
|
image: ":/icons/icons/ui/settings.svg"
|
||||||
visible: !!room
|
visible: !!room
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Settings")
|
|
||||||
onClicked: TimelineManager.openRoomSettings(room.roomId)
|
onClicked: TimelineManager.openRoomSettings(room.roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
visible: !!room
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
visible: !!room
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("%n member(s)", "", room ? room.roomMemberCount : 0)
|
text: qsTr("%n member(s)", "", room ? room.roomMemberCount : 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
image: ":/icons/icons/ui/people.svg"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("View members of %1").arg(room ? room.roomName : "")
|
ToolTip.text: qsTr("View members of %1").arg(room ? room.roomName : "")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
hoverEnabled: true
|
||||||
|
image: ":/icons/icons/ui/people.svg"
|
||||||
|
|
||||||
onClicked: TimelineManager.openRoomMembers(room)
|
onClicked: TimelineManager.openRoomMembers(room)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
@ -264,55 +238,76 @@ Item {
|
||||||
Layout.rightMargin: Nheko.paddingLarge
|
Layout.rightMargin: Nheko.paddingLarge
|
||||||
|
|
||||||
TextArea {
|
TextArea {
|
||||||
text: roomPreview.isFetched ? TimelineManager.escapeEmoji(preview.roomTopic) : qsTr("This room is possibly inaccessible. If this room is private, you should remove it from this community.")
|
|
||||||
wrapMode: TextEdit.WordWrap
|
|
||||||
textFormat: TextEdit.RichText
|
|
||||||
readOnly: true
|
|
||||||
background: null
|
background: null
|
||||||
selectByMouse: true
|
|
||||||
color: Nheko.colors.text
|
|
||||||
horizontalAlignment: TextEdit.AlignHCenter
|
horizontalAlignment: TextEdit.AlignHCenter
|
||||||
|
readOnly: true
|
||||||
|
selectByMouse: true
|
||||||
|
text: (roomPreview?.isFetched ?? false) ? TimelineManager.escapeEmoji(preview.roomTopic) : qsTr("This room is possibly inaccessible. If this room is private, you should remove it from this community.")
|
||||||
|
textFormat: TextEdit.RichText
|
||||||
|
wrapMode: TextEdit.WordWrap
|
||||||
|
|
||||||
onLinkActivated: Nheko.openLink(link)
|
onLinkActivated: Nheko.openLink(link)
|
||||||
|
|
||||||
CursorShape {
|
NhekoCursorShape {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatButton {
|
FlatButton {
|
||||||
visible: roomPreview && !roomPreview.isInvite
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("join the conversation")
|
text: qsTr("join the conversation")
|
||||||
|
visible: roomPreview && !roomPreview.isInvite
|
||||||
|
|
||||||
onClicked: Rooms.joinPreview(roomPreview.roomid)
|
onClicked: Rooms.joinPreview(roomPreview.roomid)
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatButton {
|
FlatButton {
|
||||||
visible: roomPreview && roomPreview.isInvite
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("accept invite")
|
text: qsTr("accept invite")
|
||||||
|
visible: roomPreview && roomPreview.isInvite
|
||||||
|
|
||||||
onClicked: Rooms.acceptInvite(roomPreview.roomid)
|
onClicked: Rooms.acceptInvite(roomPreview.roomid)
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatButton {
|
FlatButton {
|
||||||
visible: roomPreview && roomPreview.isInvite
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("decline invite")
|
text: qsTr("decline invite")
|
||||||
|
visible: roomPreview && roomPreview.isInvite
|
||||||
|
|
||||||
onClicked: Rooms.declineInvite(roomPreview.roomid)
|
onClicked: Rooms.declineInvite(roomPreview.roomid)
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatButton {
|
FlatButton {
|
||||||
visible: !!room
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("leave")
|
text: qsTr("leave")
|
||||||
|
visible: !!room
|
||||||
|
|
||||||
onClicked: TimelineManager.openLeaveRoomDialog(room.roomId)
|
onClicked: TimelineManager.openLeaveRoomDialog(room.roomId)
|
||||||
}
|
}
|
||||||
|
RowLayout {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
visible: roomPreview && roomPreview.isInvite && reasonField.showReason
|
||||||
|
|
||||||
|
MatrixText {
|
||||||
|
text: qsTr("Invited by %1 (%2)").arg(TimelineManager.escapeEmoji(inviterAvatar.displayName)).arg(TimelineManager.escapeEmoji(TimelineManager.htmlEscape(inviterAvatar.userid)))
|
||||||
|
}
|
||||||
|
Avatar {
|
||||||
|
id: inviterAvatar
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
displayName: roomPreview?.inviterDisplayName ?? ""
|
||||||
|
enabled: true
|
||||||
|
height: 48
|
||||||
|
roomid: preview.roomId
|
||||||
|
url: (roomPreview?.inviterAvatarUrl ?? "").replace("mxc://", "image://MxcImage/")
|
||||||
|
userid: roomPreview?.inviterUserId ?? ""
|
||||||
|
width: 48
|
||||||
|
|
||||||
|
onClicked: TimelineManager.openGlobalUserProfile(roomPreview.inviterUserId)
|
||||||
|
}
|
||||||
|
}
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id: reasonField
|
id: reasonField
|
||||||
|
|
||||||
property bool showReason: false
|
property bool showReason: false
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
@ -322,18 +317,15 @@ Item {
|
||||||
visible: preview.reason !== "" && showReason
|
visible: preview.reason !== "" && showReason
|
||||||
|
|
||||||
TextArea {
|
TextArea {
|
||||||
text: TimelineManager.escapeEmoji(preview.reason)
|
|
||||||
wrapMode: TextEdit.WordWrap
|
|
||||||
textFormat: TextEdit.RichText
|
|
||||||
readOnly: true
|
|
||||||
background: null
|
background: null
|
||||||
selectByMouse: true
|
|
||||||
color: Nheko.colors.text
|
|
||||||
horizontalAlignment: TextEdit.AlignHCenter
|
horizontalAlignment: TextEdit.AlignHCenter
|
||||||
|
readOnly: true
|
||||||
|
selectByMouse: true
|
||||||
|
text: TimelineManager.escapeEmoji(preview.reason)
|
||||||
|
textFormat: TextEdit.RichText
|
||||||
|
wrapMode: TextEdit.WordWrap
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: showReasonButton
|
id: showReasonButton
|
||||||
|
|
||||||
|
@ -341,76 +333,95 @@ Item {
|
||||||
//Layout.fillWidth: true
|
//Layout.fillWidth: true
|
||||||
Layout.leftMargin: Nheko.paddingLarge
|
Layout.leftMargin: Nheko.paddingLarge
|
||||||
Layout.rightMargin: Nheko.paddingLarge
|
Layout.rightMargin: Nheko.paddingLarge
|
||||||
|
|
||||||
visible: preview.reason !== ""
|
|
||||||
text: reasonField.showReason ? qsTr("Hide invite reason") : qsTr("Show invite reason")
|
text: reasonField.showReason ? qsTr("Hide invite reason") : qsTr("Show invite reason")
|
||||||
|
visible: roomPreview && roomPreview.isInvite
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
reasonField.showReason = !reasonField.showReason;
|
reasonField.showReason = !reasonField.showReason;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
visible: room != null
|
|
||||||
Layout.preferredHeight: Math.ceil(fontMetrics.lineSpacing * 2)
|
Layout.preferredHeight: Math.ceil(fontMetrics.lineSpacing * 2)
|
||||||
|
visible: room != null
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: backToRoomsButton
|
id: backToRoomsButton
|
||||||
|
|
||||||
anchors.top: parent.top
|
ToolTip.text: qsTr("Back to room list")
|
||||||
|
ToolTip.visible: hovered
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
width: Nheko.avatarSize
|
anchors.top: parent.top
|
||||||
height: Nheko.avatarSize
|
|
||||||
visible: (room == null || room.isSpace) && showBackButton
|
|
||||||
enabled: visible
|
enabled: visible
|
||||||
|
height: Nheko.avatarSize
|
||||||
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
||||||
ToolTip.visible: hovered
|
visible: (room == null || room.isSpace) && showBackButton
|
||||||
ToolTip.text: qsTr("Back to room list")
|
width: Nheko.avatarSize
|
||||||
|
|
||||||
onClicked: Rooms.resetCurrentRoom()
|
onClicked: Rooms.resetCurrentRoom()
|
||||||
}
|
}
|
||||||
|
|
||||||
TimelineEffects {
|
TimelineEffects {
|
||||||
id: timelineEffects
|
id: timelineEffects
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
shouldEffectsRun: timelineView.shouldEffectsRun
|
||||||
}
|
}
|
||||||
|
|
||||||
NhekoDropArea {
|
NhekoDropArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
roomid: room ? room.roomId : ""
|
roomid: room ? room.roomId : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: effectsTimer
|
id: effectsTimer
|
||||||
onTriggered: shouldEffectsRun = false;
|
|
||||||
interval: timelineEffects.maxLifespan
|
interval: timelineEffects.maxLifespan
|
||||||
repeat: false
|
repeat: false
|
||||||
running: false
|
running: false
|
||||||
}
|
|
||||||
|
|
||||||
|
onTriggered: shouldEffectsRun = false
|
||||||
|
}
|
||||||
Connections {
|
Connections {
|
||||||
|
function onConfetti() {
|
||||||
|
if (!Settings.fancyEffects)
|
||||||
|
return;
|
||||||
|
shouldEffectsRun = true;
|
||||||
|
timelineEffects.pulseConfetti();
|
||||||
|
room.markSpecialEffectsDone();
|
||||||
|
}
|
||||||
|
function onConfettiDone() {
|
||||||
|
if (!Settings.fancyEffects)
|
||||||
|
return;
|
||||||
|
effectsTimer.restart();
|
||||||
|
}
|
||||||
function onOpenReadReceiptsDialog(rr) {
|
function onOpenReadReceiptsDialog(rr) {
|
||||||
var dialog = readReceiptsDialog.createObject(timelineRoot, {
|
var dialog = readReceiptsDialog.createObject(timelineRoot, {
|
||||||
"readReceipts": rr,
|
"readReceipts": rr,
|
||||||
"room": room
|
"room": room
|
||||||
});
|
});
|
||||||
dialog.show();
|
dialog.show();
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
}
|
}
|
||||||
|
function onRainfall() {
|
||||||
|
if (!Settings.fancyEffects)
|
||||||
|
return;
|
||||||
|
shouldEffectsRun = true;
|
||||||
|
timelineEffects.pulseRainfall();
|
||||||
|
room.markSpecialEffectsDone();
|
||||||
|
}
|
||||||
|
function onRainfallDone() {
|
||||||
|
if (!Settings.fancyEffects)
|
||||||
|
return;
|
||||||
|
effectsTimer.restart();
|
||||||
|
}
|
||||||
function onShowRawMessageDialog(rawMessage) {
|
function onShowRawMessageDialog(rawMessage) {
|
||||||
var component = Qt.createComponent("qrc:/qml/dialogs/RawMessageDialog.qml")
|
var component = Qt.createComponent("qrc:/resources/qml/dialogs/RawMessageDialog.qml");
|
||||||
if (component.status == Component.Ready) {
|
if (component.status == Component.Ready) {
|
||||||
var dialog = component.createObject(timelineRoot, {
|
var dialog = component.createObject(timelineRoot, {
|
||||||
"rawMessage": rawMessage
|
"rawMessage": rawMessage
|
||||||
});
|
});
|
||||||
dialog.show();
|
dialog.show();
|
||||||
timelineRoot.destroyOnClose(dialog);
|
timelineRoot.destroyOnClose(dialog);
|
||||||
} else {
|
} else {
|
||||||
|
@ -418,43 +429,6 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onConfetti()
|
|
||||||
{
|
|
||||||
if (!Settings.fancyEffects)
|
|
||||||
return
|
|
||||||
|
|
||||||
shouldEffectsRun = true;
|
|
||||||
timelineEffects.pulseConfetti()
|
|
||||||
room.markSpecialEffectsDone()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onConfettiDone()
|
|
||||||
{
|
|
||||||
if (!Settings.fancyEffects)
|
|
||||||
return
|
|
||||||
|
|
||||||
effectsTimer.restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onRainfall()
|
|
||||||
{
|
|
||||||
if (!Settings.fancyEffects)
|
|
||||||
return
|
|
||||||
|
|
||||||
shouldEffectsRun = true;
|
|
||||||
timelineEffects.pulseRainfall()
|
|
||||||
room.markSpecialEffectsDone()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onRainfallDone()
|
|
||||||
{
|
|
||||||
if (!Settings.fancyEffects)
|
|
||||||
return
|
|
||||||
|
|
||||||
effectsTimer.restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
target: room
|
target: room
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,17 +11,44 @@ Switch {
|
||||||
id: toggleButton
|
id: toggleButton
|
||||||
|
|
||||||
implicitWidth: indicatorItem.width
|
implicitWidth: indicatorItem.width
|
||||||
|
|
||||||
state: checked ? "on" : "off"
|
state: checked ? "on" : "off"
|
||||||
|
|
||||||
|
indicator: Item {
|
||||||
|
id: indicatorItem
|
||||||
|
|
||||||
|
implicitHeight: 24
|
||||||
|
implicitWidth: 48
|
||||||
|
y: parent.height / 2 - height / 2
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: track
|
||||||
|
|
||||||
|
color: Qt.rgba(border.color.r, border.color.g, border.color.b, 0.6)
|
||||||
|
height: parent.height * 0.6
|
||||||
|
radius: height / 2
|
||||||
|
width: parent.width - height
|
||||||
|
x: radius
|
||||||
|
y: parent.height / 2 - height / 2
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
id: handle
|
||||||
|
|
||||||
|
border.color: "#767676"
|
||||||
|
color: palette.button
|
||||||
|
height: width
|
||||||
|
radius: width / 2
|
||||||
|
width: parent.height * 0.9
|
||||||
|
y: parent.height / 2 - height / 2
|
||||||
|
}
|
||||||
|
}
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
name: "off"
|
name: "off"
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: track
|
|
||||||
border.color: "#767676"
|
border.color: "#767676"
|
||||||
|
target: track
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: handle
|
target: handle
|
||||||
x: 0
|
x: 0
|
||||||
|
@ -31,10 +58,9 @@ Switch {
|
||||||
name: "on"
|
name: "on"
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
|
border.color: palette.highlight
|
||||||
target: track
|
target: track
|
||||||
border.color: Nheko.colors.highlight
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: handle
|
target: handle
|
||||||
x: indicatorItem.width - handle.width
|
x: indicatorItem.width - handle.width
|
||||||
|
@ -43,55 +69,22 @@ Switch {
|
||||||
]
|
]
|
||||||
transitions: [
|
transitions: [
|
||||||
Transition {
|
Transition {
|
||||||
to: "off"
|
|
||||||
reversible: true
|
reversible: true
|
||||||
|
to: "off"
|
||||||
|
|
||||||
ParallelAnimation {
|
ParallelAnimation {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: handle
|
|
||||||
property: "x"
|
|
||||||
duration: 200
|
duration: 200
|
||||||
easing.type: Easing.InOutQuad
|
easing.type: Easing.InOutQuad
|
||||||
|
property: "x"
|
||||||
|
target: handle
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorAnimation {
|
ColorAnimation {
|
||||||
target: track
|
|
||||||
properties: "color,border.color"
|
|
||||||
duration: 200
|
duration: 200
|
||||||
|
properties: "color,border.color"
|
||||||
|
target: track
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
indicator: Item {
|
|
||||||
id: indicatorItem
|
|
||||||
|
|
||||||
implicitWidth: 48
|
|
||||||
implicitHeight: 24
|
|
||||||
y: parent.height / 2 - height / 2
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: track
|
|
||||||
|
|
||||||
height: parent.height * 0.6
|
|
||||||
radius: height / 2
|
|
||||||
width: parent.width - height
|
|
||||||
x: radius
|
|
||||||
y: parent.height / 2 - height / 2
|
|
||||||
color: Qt.rgba(border.color.r, border.color.g, border.color.b, 0.6)
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: handle
|
|
||||||
|
|
||||||
y: parent.height / 2 - height / 2
|
|
||||||
width: parent.height * 0.9
|
|
||||||
height: width
|
|
||||||
radius: width / 2
|
|
||||||
color: Nheko.colors.button
|
|
||||||
border.color: "#767676"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,212 +8,142 @@ import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import QtQuick.Window 2.15
|
import QtQuick.Window 2.15
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
import "./delegates"
|
import "./delegates"
|
||||||
|
|
||||||
Pane {
|
Pane {
|
||||||
id: topBar
|
id: topBar
|
||||||
|
|
||||||
property bool showBackButton: false
|
|
||||||
property string roomName: room ? room.roomName : qsTr("No room selected")
|
|
||||||
property string roomId: room ? room.roomId : ""
|
|
||||||
property string avatarUrl: room ? room.roomAvatarUrl : ""
|
property string avatarUrl: room ? room.roomAvatarUrl : ""
|
||||||
property string roomTopic: room ? room.roomTopic : ""
|
|
||||||
property bool isEncrypted: room ? room.isEncrypted : false
|
|
||||||
property int trustlevel: room ? room.trustlevel : Crypto.Unverified
|
|
||||||
property bool isDirect: room ? room.isDirect : false
|
|
||||||
property string directChatOtherUserId: room ? room.directChatOtherUserId : ""
|
property string directChatOtherUserId: room ? room.directChatOtherUserId : ""
|
||||||
|
property bool isDirect: room ? room.isDirect : false
|
||||||
|
property bool isEncrypted: room ? room.isEncrypted : false
|
||||||
|
property string roomId: room ? room.roomId : ""
|
||||||
|
property string roomName: room ? room.roomName : qsTr("No room selected")
|
||||||
|
property string roomTopic: room ? room.roomTopic : ""
|
||||||
property bool searchHasFocus: searchField.focus && searchField.enabled
|
property bool searchHasFocus: searchField.focus && searchField.enabled
|
||||||
|
|
||||||
property string searchString: ""
|
property string searchString: ""
|
||||||
|
property bool showBackButton: false
|
||||||
// HACK: https://bugreports.qt.io/browse/QTBUG-83972, qtwayland cannot auto hide menu
|
property int trustlevel: room ? room.trustlevel : Crypto.Unverified
|
||||||
Connections {
|
|
||||||
function onHideMenu() {
|
|
||||||
roomOptionsMenu.close()
|
|
||||||
}
|
|
||||||
target: MainWindow
|
|
||||||
}
|
|
||||||
|
|
||||||
onRoomIdChanged: {
|
|
||||||
searchString = "";
|
|
||||||
searchButton.searchActive = false;
|
|
||||||
searchField.text = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
Shortcut {
|
|
||||||
sequence: StandardKey.Find
|
|
||||||
onActivated: searchButton.searchActive = !searchButton.searchActive
|
|
||||||
}
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
implicitHeight: topLayout.height + Nheko.paddingMedium * 2
|
implicitHeight: topLayout.height + Nheko.paddingMedium * 2
|
||||||
|
padding: 0
|
||||||
z: 3
|
z: 3
|
||||||
|
|
||||||
padding: 0
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: Nheko.colors.window
|
color: palette.window
|
||||||
}
|
}
|
||||||
|
|
||||||
TapHandler {
|
|
||||||
onSingleTapped: {
|
|
||||||
if (eventPoint.position.y > topBar.height - (pinnedMessages.visible ? pinnedMessages.height : 0) - (widgets.visible ? widgets.height : 0)) {
|
|
||||||
eventPoint.accepted = true
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (showBackButton && eventPoint.position.x < Nheko.paddingMedium + backToRoomsButton.width) {
|
|
||||||
eventPoint.accepted = true
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (eventPoint.position.x > topBar.width - Nheko.paddingMedium - roomOptionsButton.width) {
|
|
||||||
eventPoint.accepted = true
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (communityLabel.visible && eventPoint.position.y < communityAvatar.height + Nheko.paddingMedium + Nheko.paddingSmall/2) {
|
|
||||||
if (!Communities.trySwitchToSpace(room.parentSpace.roomid))
|
|
||||||
room.parentSpace.promptJoin();
|
|
||||||
eventPoint.accepted = true
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (room) {
|
|
||||||
let p = topBar.mapToItem(roomTopicC, eventPoint.position.x, eventPoint.position.y);
|
|
||||||
let link = roomTopicC.linkAt(p.x, p.y);
|
|
||||||
|
|
||||||
if (link) {
|
|
||||||
Nheko.openLink(link);
|
|
||||||
} else {
|
|
||||||
TimelineManager.openRoomSettings(room.roomId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eventPoint.accepted = true;
|
|
||||||
}
|
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
|
||||||
}
|
|
||||||
|
|
||||||
HoverHandler {
|
|
||||||
grabPermissions: PointerHandler.TakeOverForbidden | PointerHandler.CanTakeOverFromAnything
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
GridLayout {
|
GridLayout {
|
||||||
id: topLayout
|
id: topLayout
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
columnSpacing: Nheko.paddingSmall
|
columnSpacing: Nheko.paddingSmall
|
||||||
rowSpacing: Nheko.paddingSmall
|
rowSpacing: Nheko.paddingSmall
|
||||||
|
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
id: communityAvatar
|
id: communityAvatar
|
||||||
|
|
||||||
visible: roomid && room.parentSpace.isLoaded && ("space:"+room.parentSpace.roomid != Communities.currentTagId)
|
|
||||||
|
|
||||||
property string avatarUrl: (Settings.groupView && room && room.parentSpace && room.parentSpace.roomAvatarUrl) || ""
|
property string avatarUrl: (Settings.groupView && room && room.parentSpace && room.parentSpace.roomAvatarUrl) || ""
|
||||||
property string communityId: (Settings.groupView && room && room.parentSpace && room.parentSpace.roomid) || ""
|
property string communityId: (Settings.groupView && room && room.parentSpace && room.parentSpace.roomid) || ""
|
||||||
property string communityName: (Settings.groupView && room && room.parentSpace && room.parentSpace.roomName) || ""
|
property string communityName: (Settings.groupView && room && room.parentSpace && room.parentSpace.roomName) || ""
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
Layout.column: 1
|
Layout.column: 1
|
||||||
Layout.row: 0
|
Layout.row: 0
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
width: fontMetrics.lineSpacing
|
|
||||||
height: fontMetrics.lineSpacing
|
|
||||||
url: avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
roomid: communityId
|
|
||||||
displayName: communityName
|
displayName: communityName
|
||||||
enabled: false
|
enabled: false
|
||||||
|
height: fontMetrics.lineSpacing
|
||||||
|
roomid: communityId
|
||||||
|
url: avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
visible: roomid && room.parentSpace.isLoaded && ("space:" + room.parentSpace.roomid != Communities.currentTagId)
|
||||||
|
width: fontMetrics.lineSpacing
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: communityLabel
|
id: communityLabel
|
||||||
visible: communityAvatar.visible
|
|
||||||
|
|
||||||
Layout.column: 2
|
Layout.column: 2
|
||||||
Layout.row: 0
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: Nheko.colors.text
|
Layout.row: 0
|
||||||
text: qsTr("In %1").arg(communityAvatar.displayName)
|
color: palette.text
|
||||||
maximumLineCount: 1
|
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
|
maximumLineCount: 1
|
||||||
|
text: qsTr("In %1").arg(communityAvatar.displayName)
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
|
visible: communityAvatar.visible
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: backToRoomsButton
|
id: backToRoomsButton
|
||||||
|
|
||||||
Layout.column: 0
|
|
||||||
Layout.row: 1
|
|
||||||
Layout.rowSpan: 2
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.column: 0
|
||||||
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
visible: showBackButton
|
Layout.row: 1
|
||||||
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
Layout.rowSpan: 2
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Back to room list")
|
ToolTip.text: qsTr("Back to room list")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
image: ":/icons/icons/ui/angle-arrow-left.svg"
|
||||||
|
visible: showBackButton
|
||||||
|
|
||||||
onClicked: Rooms.resetCurrentRoom()
|
onClicked: Rooms.resetCurrentRoom()
|
||||||
}
|
}
|
||||||
|
|
||||||
Avatar {
|
Avatar {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.column: 1
|
Layout.column: 1
|
||||||
Layout.row: 1
|
Layout.row: 1
|
||||||
Layout.rowSpan: 2
|
Layout.rowSpan: 2
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
width: Nheko.avatarSize
|
|
||||||
height: Nheko.avatarSize
|
|
||||||
url: avatarUrl.replace("mxc://", "image://MxcImage/")
|
|
||||||
roomid: roomId
|
|
||||||
userid: isDirect ? directChatOtherUserId : ""
|
|
||||||
displayName: roomName
|
displayName: roomName
|
||||||
enabled: false
|
enabled: false
|
||||||
|
height: Nheko.avatarSize
|
||||||
|
roomid: roomId
|
||||||
|
url: avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
userid: isDirect ? directChatOtherUserId : ""
|
||||||
|
width: Nheko.avatarSize
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.column: 2
|
Layout.column: 2
|
||||||
|
Layout.fillWidth: true
|
||||||
Layout.row: 1
|
Layout.row: 1
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
|
||||||
font.bold: true
|
|
||||||
text: roomName
|
|
||||||
maximumLineCount: 1
|
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
|
font.bold: true
|
||||||
|
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||||
|
maximumLineCount: 1
|
||||||
|
text: roomName
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
id: roomTopicC
|
id: roomTopicC
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.column: 2
|
Layout.column: 2
|
||||||
Layout.row: 2
|
Layout.fillWidth: true
|
||||||
Layout.maximumHeight: fontMetrics.lineSpacing * 2 // show 2 lines
|
Layout.maximumHeight: fontMetrics.lineSpacing * 2 // show 2 lines
|
||||||
selectByMouse: false
|
Layout.row: 2
|
||||||
enabled: false
|
|
||||||
clip: true
|
clip: true
|
||||||
|
enabled: false
|
||||||
|
selectByMouse: false
|
||||||
text: roomTopic
|
text: roomTopic
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: pinButton
|
id: pinButton
|
||||||
|
|
||||||
property bool pinsShown: !Settings.hiddenPins.includes(roomId)
|
property bool pinsShown: !Settings.hiddenPins.includes(roomId)
|
||||||
|
|
||||||
visible: !!room && room.pinnedMessages.length > 0
|
|
||||||
Layout.column: 3
|
|
||||||
Layout.row: 1
|
|
||||||
Layout.rowSpan: 2
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.column: 3
|
||||||
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
image: pinsShown ? ":/icons/icons/ui/pin.svg" : ":/icons/icons/ui/pin-off.svg"
|
Layout.row: 1
|
||||||
ToolTip.visible: hovered
|
Layout.rowSpan: 2
|
||||||
ToolTip.text: qsTr("Show or hide pinned messages")
|
ToolTip.text: qsTr("Show or hide pinned messages")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
image: pinsShown ? ":/icons/icons/ui/pin.svg" : ":/icons/icons/ui/pin-off.svg"
|
||||||
|
visible: !!room && room.pinnedMessages.length > 0
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var ps = Settings.hiddenPins;
|
var ps = Settings.hiddenPins;
|
||||||
if (pinsShown) {
|
if (pinsShown) {
|
||||||
|
@ -226,254 +156,280 @@ Pane {
|
||||||
}
|
}
|
||||||
Settings.hiddenPins = ps;
|
Settings.hiddenPins = ps;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
Layout.column: 4
|
Layout.column: 4
|
||||||
Layout.row: 1
|
|
||||||
Layout.rowSpan: 2
|
|
||||||
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
|
Layout.row: 1
|
||||||
|
Layout.rowSpan: 2
|
||||||
|
background: null
|
||||||
|
|
||||||
contentItem: EncryptionIndicator {
|
contentItem: EncryptionIndicator {
|
||||||
encrypted: isEncrypted
|
|
||||||
trust: trustlevel
|
|
||||||
enabled: false
|
|
||||||
unencryptedIcon: ":/icons/icons/ui/people.svg"
|
|
||||||
unencryptedColor: Nheko.colors.buttonText
|
|
||||||
unencryptedHoverColor: Nheko.colors.highlight
|
|
||||||
hovered: parent.hovered
|
|
||||||
|
|
||||||
ToolTip.delay: Nheko.tooltipDelay
|
ToolTip.delay: Nheko.tooltipDelay
|
||||||
ToolTip.text: {
|
ToolTip.text: {
|
||||||
if (!isEncrypted)
|
if (!isEncrypted)
|
||||||
return qsTr("Show room members.");
|
return qsTr("Show room members.");
|
||||||
|
|
||||||
switch (trustlevel) {
|
switch (trustlevel) {
|
||||||
case Crypto.Verified:
|
case Crypto.Verified:
|
||||||
return qsTr("This room contains only verified devices.");
|
return qsTr("This room contains only verified devices.");
|
||||||
case Crypto.TOFU:
|
case Crypto.TOFU:
|
||||||
return qsTr("This room contains verified devices and devices which have never changed their master key.");
|
return qsTr("This room contains verified devices and devices which have never changed their master key.");
|
||||||
default:
|
default:
|
||||||
return qsTr("This room contains unverified devices!");
|
return qsTr("This room contains unverified devices!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
enabled: false
|
||||||
|
encrypted: isEncrypted
|
||||||
|
hovered: parent.hovered
|
||||||
|
trust: trustlevel
|
||||||
|
unencryptedColor: palette.buttonText
|
||||||
|
unencryptedHoverColor: palette.highlight
|
||||||
|
unencryptedIcon: ":/icons/icons/ui/people.svg"
|
||||||
}
|
}
|
||||||
|
|
||||||
background: null
|
|
||||||
onClicked: TimelineManager.openRoomMembers(room)
|
onClicked: TimelineManager.openRoomMembers(room)
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: searchButton
|
id: searchButton
|
||||||
|
|
||||||
property bool searchActive: false
|
property bool searchActive: false
|
||||||
|
|
||||||
visible: !!room
|
|
||||||
Layout.column: 5
|
|
||||||
Layout.row: 1
|
|
||||||
Layout.rowSpan: 2
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.column: 5
|
||||||
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
image: ":/icons/icons/ui/search.svg"
|
Layout.row: 1
|
||||||
ToolTip.visible: hovered
|
Layout.rowSpan: 2
|
||||||
ToolTip.text: qsTr("Search this room")
|
ToolTip.text: qsTr("Search this room")
|
||||||
onClicked: searchActive = !searchActive
|
ToolTip.visible: hovered
|
||||||
|
image: ":/icons/icons/ui/search.svg"
|
||||||
|
visible: !!room
|
||||||
|
|
||||||
|
onClicked: searchActive = !searchActive
|
||||||
onSearchActiveChanged: {
|
onSearchActiveChanged: {
|
||||||
if (searchActive) {
|
if (searchActive) {
|
||||||
searchField.forceActiveFocus();
|
searchField.forceActiveFocus();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
searchField.clear();
|
searchField.clear();
|
||||||
topBar.searchString = "";
|
topBar.searchString = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: roomOptionsButton
|
id: roomOptionsButton
|
||||||
|
|
||||||
visible: !!room
|
|
||||||
Layout.column: 6
|
|
||||||
Layout.row: 1
|
|
||||||
Layout.rowSpan: 2
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.column: 6
|
||||||
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
|
||||||
image: ":/icons/icons/ui/options.svg"
|
Layout.row: 1
|
||||||
ToolTip.visible: hovered
|
Layout.rowSpan: 2
|
||||||
ToolTip.text: qsTr("Room options")
|
ToolTip.text: qsTr("Room options")
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
image: ":/icons/icons/ui/options.svg"
|
||||||
|
visible: !!room
|
||||||
|
|
||||||
onClicked: roomOptionsMenu.open(roomOptionsButton)
|
onClicked: roomOptionsMenu.open(roomOptionsButton)
|
||||||
|
|
||||||
Platform.Menu {
|
Platform.Menu {
|
||||||
id: roomOptionsMenu
|
id: roomOptionsMenu
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
visible: room ? room.permissions.canInvite() : false
|
|
||||||
text: qsTr("Invite users")
|
text: qsTr("Invite users")
|
||||||
|
visible: room ? room.permissions.canInvite() : false
|
||||||
|
|
||||||
onTriggered: TimelineManager.openInviteUsers(roomId)
|
onTriggered: TimelineManager.openInviteUsers(roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Members")
|
text: qsTr("Members")
|
||||||
|
|
||||||
onTriggered: TimelineManager.openRoomMembers(room)
|
onTriggered: TimelineManager.openRoomMembers(room)
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Leave room")
|
text: qsTr("Leave room")
|
||||||
|
|
||||||
onTriggered: TimelineManager.openLeaveRoomDialog(roomId)
|
onTriggered: TimelineManager.openLeaveRoomDialog(roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Settings")
|
text: qsTr("Settings")
|
||||||
|
|
||||||
onTriggered: TimelineManager.openRoomSettings(roomId)
|
onTriggered: TimelineManager.openRoomSettings(roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id: pinnedMessages
|
id: pinnedMessages
|
||||||
|
|
||||||
Layout.row: 3
|
|
||||||
Layout.column: 2
|
Layout.column: 2
|
||||||
Layout.columnSpan: 4
|
Layout.columnSpan: 4
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 4)
|
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 4)
|
||||||
|
Layout.row: 3
|
||||||
visible: !!room && room.pinnedMessages.length > 0 && !Settings.hiddenPins.includes(roomId)
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
palette: Nheko.colors
|
|
||||||
ScrollBar.horizontal.visible: false
|
ScrollBar.horizontal.visible: false
|
||||||
|
clip: true
|
||||||
|
visible: !!room && room.pinnedMessages.length > 0 && !Settings.hiddenPins.includes(roomId)
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
|
|
||||||
spacing: Nheko.paddingSmall
|
|
||||||
model: room ? room.pinnedMessages : undefined
|
model: room ? room.pinnedMessages : undefined
|
||||||
|
spacing: Nheko.paddingSmall
|
||||||
|
|
||||||
delegate: RowLayout {
|
delegate: RowLayout {
|
||||||
required property string modelData
|
required property string modelData
|
||||||
|
|
||||||
width: ListView.view.width
|
|
||||||
height: implicitHeight
|
height: implicitHeight
|
||||||
|
width: ListView.view.width
|
||||||
|
|
||||||
Reply {
|
Reply {
|
||||||
id: reply
|
id: reply
|
||||||
|
|
||||||
property var e: room ? room.getDump(modelData, "pins") : {}
|
property var e: room ? room.getDump(modelData, "pins") : {}
|
||||||
Connections {
|
|
||||||
function onPinnedMessagesChanged() { reply.e = room.getDump(modelData, "pins") }
|
|
||||||
target: room
|
|
||||||
}
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: height
|
Layout.preferredHeight: height
|
||||||
|
|
||||||
userColor: TimelineManager.userColor(e.userId, Nheko.colors.window)
|
|
||||||
blurhash: e.blurhash ?? ""
|
blurhash: e.blurhash ?? ""
|
||||||
body: e.body ?? ""
|
body: e.body ?? ""
|
||||||
formattedBody: e.formattedBody ?? ""
|
encryptionError: e.encryptionError ?? 0
|
||||||
eventId: e.eventId ?? ""
|
eventId: e.eventId ?? ""
|
||||||
filename: e.filename ?? ""
|
filename: e.filename ?? ""
|
||||||
filesize: e.filesize ?? ""
|
filesize: e.filesize ?? ""
|
||||||
|
formattedBody: e.formattedBody ?? ""
|
||||||
|
isOnlyEmoji: e.isOnlyEmoji ?? false
|
||||||
|
keepFullText: true
|
||||||
|
originalWidth: e.originalWidth ?? 0
|
||||||
proportionalHeight: e.proportionalHeight ?? 1
|
proportionalHeight: e.proportionalHeight ?? 1
|
||||||
type: e.type ?? MtxEvent.UnknownMessage
|
type: e.type ?? MtxEvent.UnknownMessage
|
||||||
typeString: e.typeString ?? ""
|
typeString: e.typeString ?? ""
|
||||||
url: e.url ?? ""
|
url: e.url ?? ""
|
||||||
originalWidth: e.originalWidth ?? 0
|
userColor: TimelineManager.userColor(e.userId, palette.window)
|
||||||
isOnlyEmoji: e.isOnlyEmoji ?? false
|
|
||||||
userId: e.userId ?? ""
|
userId: e.userId ?? ""
|
||||||
userName: e.userName ?? ""
|
userName: e.userName ?? ""
|
||||||
encryptionError: e.encryptionError ?? ""
|
|
||||||
keepFullText: true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
function onPinnedMessagesChanged() {
|
||||||
|
reply.e = room.getDump(modelData, "pins");
|
||||||
|
}
|
||||||
|
|
||||||
|
target: room
|
||||||
|
}
|
||||||
|
}
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: deletePinButton
|
id: deletePinButton
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||||
Layout.preferredHeight: 16
|
Layout.preferredHeight: 16
|
||||||
Layout.preferredWidth: 16
|
Layout.preferredWidth: 16
|
||||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
ToolTip.text: qsTr("Unpin")
|
||||||
visible: room.permissions.canChange(MtxEvent.PinnedEvents)
|
ToolTip.visible: hovered
|
||||||
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/dismiss.svg"
|
image: ":/icons/icons/ui/dismiss.svg"
|
||||||
ToolTip.visible: hovered
|
visible: room.permissions.canChange(MtxEvent.PinnedEvents)
|
||||||
ToolTip.text: qsTr("Unpin")
|
|
||||||
|
|
||||||
onClicked: room.unpin(modelData)
|
onClicked: room.unpin(modelData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ScrollHelper {
|
|
||||||
flickable: parent
|
|
||||||
anchors.fill: parent
|
|
||||||
enabled: !Settings.mobileMode
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id: widgets
|
id: widgets
|
||||||
|
|
||||||
Layout.row: 4
|
|
||||||
Layout.column: 2
|
Layout.column: 2
|
||||||
Layout.columnSpan: 4
|
Layout.columnSpan: 4
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 1.5)
|
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 1.5)
|
||||||
|
Layout.row: 4
|
||||||
visible: !!room && room.widgetLinks.length > 0 && !Settings.hiddenWidgets.includes(roomId)
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
palette: Nheko.colors
|
|
||||||
ScrollBar.horizontal.visible: false
|
ScrollBar.horizontal.visible: false
|
||||||
|
clip: true
|
||||||
|
visible: !!room && room.widgetLinks.length > 0 && !Settings.hiddenWidgets.includes(roomId)
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
|
|
||||||
spacing: Nheko.paddingSmall
|
|
||||||
model: room ? room.widgetLinks : undefined
|
model: room ? room.widgetLinks : undefined
|
||||||
|
spacing: Nheko.paddingSmall
|
||||||
|
|
||||||
delegate: MatrixText {
|
delegate: MatrixText {
|
||||||
required property var modelData
|
required property var modelData
|
||||||
|
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
text: modelData
|
text: modelData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ScrollHelper {
|
|
||||||
flickable: parent
|
|
||||||
anchors.fill: parent
|
|
||||||
enabled: !Settings.mobileMode
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: searchField
|
id: searchField
|
||||||
visible: searchButton.searchActive
|
|
||||||
enabled: visible
|
|
||||||
hasClear: true
|
|
||||||
|
|
||||||
Layout.row: 5
|
|
||||||
Layout.column: 2
|
Layout.column: 2
|
||||||
Layout.columnSpan: 4
|
Layout.columnSpan: 4
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.row: 5
|
||||||
|
enabled: visible
|
||||||
|
hasClear: true
|
||||||
placeholderText: qsTr("Enter search query")
|
placeholderText: qsTr("Enter search query")
|
||||||
|
visible: searchButton.searchActive
|
||||||
|
|
||||||
onAccepted: topBar.searchString = text
|
onAccepted: topBar.searchString = text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
NhekoCursorShape {
|
||||||
CursorShape {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.bottomMargin: (pinnedMessages.visible ? pinnedMessages.height : 0) + (widgets.visible ? widgets.height : 0)
|
anchors.bottomMargin: (pinnedMessages.visible ? pinnedMessages.height : 0) + (widgets.visible ? widgets.height : 0)
|
||||||
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onRoomIdChanged: {
|
||||||
|
searchString = "";
|
||||||
|
searchButton.searchActive = false;
|
||||||
|
searchField.text = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// HACK: https://bugreports.qt.io/browse/QTBUG-83972, qtwayland cannot auto hide menu
|
||||||
|
Connections {
|
||||||
|
function onHideMenu() {
|
||||||
|
roomOptionsMenu.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
target: MainWindow
|
||||||
|
}
|
||||||
|
Shortcut {
|
||||||
|
sequence: StandardKey.Find
|
||||||
|
|
||||||
|
onActivated: searchButton.searchActive = !searchButton.searchActive
|
||||||
|
}
|
||||||
|
TapHandler {
|
||||||
|
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||||
|
|
||||||
|
onSingleTapped: {
|
||||||
|
if (eventPoint.position.y > topBar.height - (pinnedMessages.visible ? pinnedMessages.height : 0) - (widgets.visible ? widgets.height : 0)) {
|
||||||
|
eventPoint.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (showBackButton && eventPoint.position.x < Nheko.paddingMedium + backToRoomsButton.width) {
|
||||||
|
eventPoint.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (eventPoint.position.x > topBar.width - Nheko.paddingMedium - roomOptionsButton.width) {
|
||||||
|
eventPoint.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (communityLabel.visible && eventPoint.position.y < communityAvatar.height + Nheko.paddingMedium + Nheko.paddingSmall / 2) {
|
||||||
|
if (!Communities.trySwitchToSpace(room.parentSpace.roomid))
|
||||||
|
room.parentSpace.promptJoin();
|
||||||
|
eventPoint.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (room) {
|
||||||
|
let p = topBar.mapToItem(roomTopicC, eventPoint.position.x, eventPoint.position.y);
|
||||||
|
let link = roomTopicC.linkAt(p.x, p.y);
|
||||||
|
if (link) {
|
||||||
|
Nheko.openLink(link);
|
||||||
|
} else {
|
||||||
|
TimelineManager.openRoomSettings(room.roomId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eventPoint.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HoverHandler {
|
||||||
|
grabPermissions: PointerHandler.TakeOverForbidden | PointerHandler.CanTakeOverFromAnything
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,30 +8,28 @@ import QtQuick.Layouts 1.2
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
implicitHeight: Math.max(fontMetrics.height * 1.2, typingDisplay.height)
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: Math.max(fontMetrics.height * 1.2, typingDisplay.height)
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: typingRect
|
id: typingRect
|
||||||
|
|
||||||
visible: (room && room.typingUsers.length > 0)
|
|
||||||
color: Nheko.colors.base
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
color: palette.base
|
||||||
|
visible: (room && room.typingUsers.length > 0)
|
||||||
z: 3
|
z: 3
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: typingDisplay
|
id: typingDisplay
|
||||||
|
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: 10
|
anchors.leftMargin: 10
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: 10
|
anchors.rightMargin: 10
|
||||||
anchors.bottom: parent.bottom
|
color: palette.text
|
||||||
color: Nheko.colors.text
|
text: room ? room.formatTypingUsers(room.typingUsers, palette.base) : ""
|
||||||
text: room ? room.formatTypingUsers(room.typingUsers, Nheko.colors.base) : ""
|
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
import "./components"
|
import "./components"
|
||||||
import "./ui"
|
import "./ui"
|
||||||
|
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.5
|
import QtQuick.Controls 2.5
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
|
@ -12,79 +11,85 @@ import im.nheko 1.0
|
||||||
|
|
||||||
Page {
|
Page {
|
||||||
id: uploadPopup
|
id: uploadPopup
|
||||||
visible: room && room.input.uploads.length > 0
|
|
||||||
Layout.preferredHeight: 200
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 200
|
||||||
|
clip: true
|
||||||
padding: Nheko.paddingMedium
|
padding: Nheko.paddingMedium
|
||||||
|
visible: room && room.input.uploads.length > 0
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: palette.base
|
||||||
|
}
|
||||||
contentItem: ListView {
|
contentItem: ListView {
|
||||||
id: uploadsList
|
id: uploadsList
|
||||||
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
model: room ? room.input.uploads : undefined
|
||||||
|
orientation: ListView.Horizontal
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
width: Math.min(contentWidth, parent.availableWidth)
|
||||||
|
|
||||||
ScrollBar.horizontal: ScrollBar {
|
ScrollBar.horizontal: ScrollBar {
|
||||||
id: scr
|
id: scr
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
orientation: ListView.Horizontal
|
|
||||||
width: Math.min(contentWidth, parent.availableWidth)
|
|
||||||
model: room ? room.input.uploads : undefined
|
|
||||||
spacing: Nheko.paddingMedium
|
|
||||||
|
|
||||||
delegate: Pane {
|
delegate: Pane {
|
||||||
|
id: pane
|
||||||
|
|
||||||
|
height: uploadPopup.availableHeight - buttons.height - (scr.visible ? scr.height : 0)
|
||||||
padding: Nheko.paddingSmall
|
padding: Nheko.paddingSmall
|
||||||
height: uploadPopup.availableHeight - buttons.height - (scr.visible? scr.height : 0)
|
|
||||||
width: uploadPopup.availableHeight - buttons.height
|
width: uploadPopup.availableHeight - buttons.height
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: Nheko.colors.window
|
color: palette.window
|
||||||
radius: Nheko.paddingMedium
|
radius: Nheko.paddingMedium
|
||||||
}
|
}
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
Image {
|
Image {
|
||||||
|
property string typeStr: switch (modelData.mediaType) {
|
||||||
|
case MediaUpload.Video:
|
||||||
|
return "video-file";
|
||||||
|
case MediaUpload.Audio:
|
||||||
|
return "music";
|
||||||
|
case MediaUpload.Image:
|
||||||
|
return "image";
|
||||||
|
default:
|
||||||
|
return "zip";
|
||||||
|
}
|
||||||
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
sourceSize.height: parent.availableHeight - namefield.height
|
|
||||||
sourceSize.width: parent.availableWidth
|
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
smooth: true
|
|
||||||
mipmap: true
|
mipmap: true
|
||||||
|
smooth: true
|
||||||
property string typeStr: switch(modelData.mediaType) {
|
source: (modelData.thumbnail != "") ? modelData.thumbnail : ("image://colorimage/:/icons/icons/ui/" + typeStr + ".svg?" + palette.buttonText)
|
||||||
case MediaUpload.Video: return "video-file";
|
sourceSize.height: pane.availableHeight - namefield.height
|
||||||
case MediaUpload.Audio: return "music";
|
sourceSize.width: pane.availableWidth
|
||||||
case MediaUpload.Image: return "image";
|
|
||||||
default: return "zip";
|
|
||||||
}
|
|
||||||
source: (modelData.thumbnail != "") ? modelData.thumbnail : ("image://colorimage/:/icons/icons/ui/"+typeStr+".svg?" + Nheko.colors.buttonText)
|
|
||||||
}
|
}
|
||||||
MatrixTextField {
|
MatrixTextField {
|
||||||
id: namefield
|
id: namefield
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: modelData.filename
|
text: modelData.filename
|
||||||
|
|
||||||
onTextEdited: modelData.filename = text
|
onTextEdited: modelData.filename = text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: DialogButtonBox {
|
footer: DialogButtonBox {
|
||||||
id: buttons
|
id: buttons
|
||||||
|
|
||||||
standardButtons: DialogButtonBox.Cancel
|
standardButtons: DialogButtonBox.Cancel
|
||||||
Button {
|
|
||||||
text: qsTr("Upload %n file(s)", "", (room ? room.input.uploads.length : 0))
|
|
||||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
|
||||||
}
|
|
||||||
onAccepted: room.input.acceptUploads()
|
onAccepted: room.input.acceptUploads()
|
||||||
onRejected: room.input.declineUploads()
|
onRejected: room.input.declineUploads()
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
Button {
|
||||||
color: Nheko.colors.base
|
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||||
|
text: qsTr("Upload %n file(s)", "", (room ? room.input.uploads.length : 0))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ Container {
|
||||||
x: parent.preferredWidth
|
x: parent.preferredWidth
|
||||||
z: 3
|
z: 3
|
||||||
|
|
||||||
CursorShape {
|
NhekoCursorShape {
|
||||||
height: parent.height
|
height: parent.height
|
||||||
width: container.splitterGrabMargin * 2
|
width: container.splitterGrabMargin * 2
|
||||||
x: -container.splitterGrabMargin
|
x: -container.splitterGrabMargin
|
||||||
|
|
|
@ -11,11 +11,11 @@ import im.nheko 1.0
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: tile
|
id: tile
|
||||||
|
|
||||||
property color background: Nheko.colors.window
|
property color background: palette.window
|
||||||
property color importantText: Nheko.colors.text
|
property color importantText: palette.text
|
||||||
property color unimportantText: Nheko.colors.buttonText
|
property color unimportantText: palette.buttonText
|
||||||
property color bubbleBackground: Nheko.colors.highlight
|
property color bubbleBackground: palette.highlight
|
||||||
property color bubbleText: Nheko.colors.highlightedText
|
property color bubbleText: palette.highlightedText
|
||||||
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
|
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
|
||||||
required property string avatarUrl
|
required property string avatarUrl
|
||||||
required property string title
|
required property string title
|
||||||
|
@ -37,11 +37,11 @@ Rectangle {
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: tile
|
target: tile
|
||||||
background: Nheko.colors.dark
|
background: palette.dark
|
||||||
importantText: Nheko.colors.brightText
|
importantText: palette.brightText
|
||||||
unimportantText: Nheko.colors.brightText
|
unimportantText: palette.brightText
|
||||||
bubbleBackground: Nheko.colors.highlight
|
bubbleBackground: palette.highlight
|
||||||
bubbleText: Nheko.colors.highlightedText
|
bubbleText: palette.highlightedText
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
@ -51,11 +51,11 @@ Rectangle {
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: tile
|
target: tile
|
||||||
background: Nheko.colors.highlight
|
background: palette.highlight
|
||||||
importantText: Nheko.colors.highlightedText
|
importantText: palette.highlightedText
|
||||||
unimportantText: Nheko.colors.highlightedText
|
unimportantText: palette.highlightedText
|
||||||
bubbleBackground: Nheko.colors.highlightedText
|
bubbleBackground: palette.highlightedText
|
||||||
bubbleText: Nheko.colors.highlight
|
bubbleText: palette.highlight
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import QtGraphicalEffects 1.12
|
import QtQuick
|
||||||
import QtQuick 2.9
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls 2.5
|
import QtQuick.Layouts
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Effects
|
||||||
import im.nheko 1.0
|
import im.nheko
|
||||||
|
|
||||||
// FIXME(Nico): Don't use hardcoded colors.
|
// FIXME(Nico): Don't use hardcoded colors.
|
||||||
Button {
|
Button {
|
||||||
|
@ -18,14 +18,13 @@ Button {
|
||||||
|
|
||||||
property string iconImage: ""
|
property string iconImage: ""
|
||||||
|
|
||||||
DropShadow {
|
MultiEffect {
|
||||||
anchors.fill: control.background
|
anchors.fill: control.background
|
||||||
horizontalOffset: 3
|
shadowHorizontalOffset: 3
|
||||||
verticalOffset: 3
|
shadowVerticalOffset: 3
|
||||||
radius: 8
|
shadowBlur: 8
|
||||||
samples: 17
|
shadowEnabled: true
|
||||||
cached: true
|
shadowColor: "#80000000"
|
||||||
color: "#80000000"
|
|
||||||
source: control.background
|
source: control.background
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +47,7 @@ Button {
|
||||||
font.capitalization: Font.AllUppercase
|
font.capitalization: Font.AllUppercase
|
||||||
font.pointSize: Math.ceil(fontMetrics.font.pointSize * 1.5)
|
font.pointSize: Math.ceil(fontMetrics.font.pointSize * 1.5)
|
||||||
//font.capitalization: Font.AllUppercase
|
//font.capitalization: Font.AllUppercase
|
||||||
color: Nheko.colors.light
|
color: palette.light
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
|
@ -59,7 +58,7 @@ Button {
|
||||||
//height: control.contentItem.implicitHeight * 2
|
//height: control.contentItem.implicitHeight * 2
|
||||||
//width: control.contentItem.implicitWidth * 2
|
//width: control.contentItem.implicitWidth * 2
|
||||||
radius: height / 8
|
radius: height / 8
|
||||||
color: Qt.lighter(Nheko.colors.dark, control.down ? 1.4 : (control.hovered ? 1.2 : 1))
|
color: Qt.lighter(palette.dark, control.down ? 1.4 : (control.hovered ? 1.2 : 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ Dialog {
|
||||||
]
|
]
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: Nheko.colors.window
|
color: palette.window
|
||||||
border.color: Nheko.theme.separator
|
border.color: Nheko.theme.separator
|
||||||
border.width: 1
|
border.width: 1
|
||||||
radius: Nheko.paddingSmall
|
radius: Nheko.paddingSmall
|
||||||
|
|
|
@ -13,15 +13,15 @@ TabButton {
|
||||||
text: control.text
|
text: control.text
|
||||||
font: control.font
|
font: control.font
|
||||||
opacity: enabled ? 1.0 : 0.3
|
opacity: enabled ? 1.0 : 0.3
|
||||||
color: control.down ? Nheko.colors.highlightedText : Nheko.colors.text
|
color: control.down ? palette.highlightedText : palette.text
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
border.color: control.down ? Nheko.colors.highlight : Nheko.theme.separator
|
border.color: control.down ? palette.highlight : Nheko.theme.separator
|
||||||
color: control.checked ? Nheko.colors.highlight : Nheko.colors.base
|
color: control.checked ? palette.highlight : palette.base
|
||||||
border.width: 1
|
border.width: 1
|
||||||
radius: 2
|
radius: 2
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ Rectangle {
|
||||||
required property color bubbleTextColor
|
required property color bubbleTextColor
|
||||||
property bool mayBeVisible: true
|
property bool mayBeVisible: true
|
||||||
property alias font: notificationBubbleText.font
|
property alias font: notificationBubbleText.font
|
||||||
|
baselineOffset: notificationBubbleText.baseline - bubbleRoot.top
|
||||||
|
|
||||||
visible: mayBeVisible && notificationCount > 0
|
visible: mayBeVisible && notificationCount > 0
|
||||||
implicitHeight: notificationBubbleText.height + Nheko.paddingMedium
|
implicitHeight: notificationBubbleText.height + Nheko.paddingMedium
|
||||||
|
|
|
@ -47,9 +47,9 @@ Item {
|
||||||
width: dragArea.width; height: actualDelegate.implicitHeight + 4
|
width: dragArea.width; height: actualDelegate.implicitHeight + 4
|
||||||
|
|
||||||
border.width: dragArea.enabled ? 1 : 0
|
border.width: dragArea.enabled ? 1 : 0
|
||||||
border.color: Nheko.colors.highlight
|
border.color: palette.highlight
|
||||||
|
|
||||||
color: dragArea.held ? Nheko.colors.highlight : Nheko.colors.base
|
color: dragArea.held ? palette.highlight : palette.base
|
||||||
Behavior on color { ColorAnimation { duration: 100 } }
|
Behavior on color { ColorAnimation { duration: 100 } }
|
||||||
|
|
||||||
radius: 2
|
radius: 2
|
||||||
|
@ -105,10 +105,6 @@ Item {
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
anchors { fill: parent; margins: 2 }
|
anchors { fill: parent; margins: 2 }
|
||||||
ScrollHelper {
|
|
||||||
flickable: parent
|
|
||||||
anchors.fill: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
model: visualModel
|
model: visualModel
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@ AbstractButton {
|
||||||
id: button
|
id: button
|
||||||
|
|
||||||
property alias cursor: mouseArea.cursorShape
|
property alias cursor: mouseArea.cursorShape
|
||||||
property color highlightColor: Nheko.colors.highlight
|
property color highlightColor: palette.highlight
|
||||||
property color buttonTextColor: Nheko.colors.buttonText
|
property color buttonTextColor: palette.buttonText
|
||||||
|
|
||||||
focusPolicy: Qt.NoFocus
|
focusPolicy: Qt.NoFocus
|
||||||
width: buttonText.implicitWidth
|
width: buttonText.implicitWidth
|
||||||
|
@ -32,7 +32,7 @@ AbstractButton {
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
CursorShape {
|
NhekoCursorShape {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
|
@ -36,7 +36,7 @@ ItemDelegate {
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: displayName
|
text: displayName
|
||||||
color: TimelineManager.userColor(userid, Nheko.colors.window)
|
color: TimelineManager.userColor(userid, palette.window)
|
||||||
font.pointSize: fontMetrics.font.pointSize
|
font.pointSize: fontMetrics.font.pointSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ ItemDelegate {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
text: userid
|
text: userid
|
||||||
color: Nheko.colors.buttonText
|
color: palette.buttonText
|
||||||
font.pointSize: fontMetrics.font.pointSize * 0.9
|
font.pointSize: fontMetrics.font.pointSize * 0.9
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ Rectangle {
|
||||||
width: parent.width? parent.width : 0
|
width: parent.width? parent.width : 0
|
||||||
implicitWidth: encryptedText.implicitWidth+24+Nheko.paddingMedium*3 // Column doesn't provide a useful implicitWidth, should be replaced by ColumnLayout
|
implicitWidth: encryptedText.implicitWidth+24+Nheko.paddingMedium*3 // Column doesn't provide a useful implicitWidth, should be replaced by ColumnLayout
|
||||||
height: contents.implicitHeight + Nheko.paddingMedium * 2
|
height: contents.implicitHeight + Nheko.paddingMedium * 2
|
||||||
color: Nheko.colors.alternateBase
|
color: palette.alternateBase
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: contents
|
id: contents
|
||||||
|
@ -58,12 +58,11 @@ Rectangle {
|
||||||
return qsTr("Unknown decryption error");
|
return qsTr("Unknown decryption error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
palette: Nheko.colors
|
|
||||||
visible: encryptionError == Olm.MissingSession || encryptionError == Olm.MissingSessionIndex
|
visible: encryptionError == Olm.MissingSession || encryptionError == Olm.MissingSessionIndex
|
||||||
text: qsTr("Request key")
|
text: qsTr("Request key")
|
||||||
onClicked: room.requestKeyForEvent(eventId)
|
onClicked: room.requestKeyForEvent(eventId)
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
import ".."
|
import ".."
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
|
@ -15,9 +14,8 @@ Rectangle {
|
||||||
|
|
||||||
radius: fontMetrics.lineSpacing / 2 + Nheko.paddingMedium
|
radius: fontMetrics.lineSpacing / 2 + Nheko.paddingMedium
|
||||||
width: parent.width ? Math.min(parent.width, 700) : 0
|
width: parent.width ? Math.min(parent.width, 700) : 0
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
height: contents.implicitHeight + Nheko.paddingMedium * 2
|
height: contents.implicitHeight + Nheko.paddingMedium * 2
|
||||||
color: Nheko.colors.alternateBase
|
color: palette.alternateBase
|
||||||
border.color: Nheko.theme.green
|
border.color: Nheko.theme.green
|
||||||
border.width: 2
|
border.width: 2
|
||||||
|
|
||||||
|
@ -31,8 +29,8 @@ Rectangle {
|
||||||
Image {
|
Image {
|
||||||
source: "image://colorimage/:/icons/icons/ui/shield-filled-checkmark.svg?" + Nheko.theme.green
|
source: "image://colorimage/:/icons/icons/ui/shield-filled-checkmark.svg?" + Nheko.theme.green
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
width: 24
|
Layout.preferredWidth: 24
|
||||||
height: width
|
Layout.preferredHeight: 24
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
@ -43,13 +41,13 @@ Rectangle {
|
||||||
text: qsTr("%1 enabled end-to-end encryption").arg(r.username)
|
text: qsTr("%1 enabled end-to-end encryption").arg(r.username)
|
||||||
font.bold: true
|
font.bold: true
|
||||||
font.pointSize: 14
|
font.pointSize: 14
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixText {
|
MatrixText {
|
||||||
text: qsTr("Encryption keeps your messages safe by only allowing the people you sent the message to to read it. For extra security, if you want to make sure you are talking to the right people, you can verify them in real life.")
|
text: qsTr("Encryption keeps your messages safe by only allowing the people you sent the message to to read it. For extra security, if you want to make sure you are talking to the right people, you can verify them in real life.")
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,14 +11,13 @@ Item {
|
||||||
required property string filename
|
required property string filename
|
||||||
required property string filesize
|
required property string filesize
|
||||||
|
|
||||||
height: row.height + (Settings.bubbles? 16: 24)
|
height: rowa.height + (Settings.bubbles? 16: 24)
|
||||||
width: parent.width
|
implicitWidth: rowa.implicitWidth + metadataWidth
|
||||||
implicitWidth: row.implicitWidth+metadataWidth
|
|
||||||
property int metadataWidth
|
property int metadataWidth
|
||||||
property bool fitsMetadata: true
|
property bool fitsMetadata: true
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: row
|
id: rowa
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: parent.width - (Settings.bubbles? 16 : 24)
|
width: parent.width - (Settings.bubbles? 16 : 24)
|
||||||
|
@ -27,7 +26,7 @@ Item {
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: button
|
id: button
|
||||||
|
|
||||||
color: Nheko.colors.light
|
color: palette.light
|
||||||
radius: 22
|
radius: 22
|
||||||
height: 44
|
height: 44
|
||||||
width: 44
|
width: 44
|
||||||
|
@ -50,7 +49,7 @@ Item {
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||||
}
|
}
|
||||||
|
|
||||||
CursorShape {
|
NhekoCursorShape {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
@ -67,7 +66,7 @@ Item {
|
||||||
text: filename
|
text: filename
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
|
@ -77,7 +76,7 @@ Item {
|
||||||
text: filesize
|
text: filesize
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -85,7 +84,7 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
color: Nheko.colors.alternateBase
|
color: palette.alternateBase
|
||||||
z: -1
|
z: -1
|
||||||
radius: 10
|
radius: 10
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
|
@ -22,7 +22,7 @@ AbstractButton {
|
||||||
property int tempWidth: originalWidth < 1? 400: originalWidth
|
property int tempWidth: originalWidth < 1? 400: originalWidth
|
||||||
|
|
||||||
implicitWidth: Math.round(tempWidth*Math.min((timelineView.height/divisor)/(tempWidth*proportionalHeight), 1))
|
implicitWidth: Math.round(tempWidth*Math.min((timelineView.height/divisor)/(tempWidth*proportionalHeight), 1))
|
||||||
width: Math.min(parent.width,implicitWidth)
|
width: Math.min(parent?.width ?? 2000,implicitWidth)
|
||||||
height: width*proportionalHeight
|
height: width*proportionalHeight
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
|
||||||
|
@ -106,14 +106,14 @@ AbstractButton {
|
||||||
]
|
]
|
||||||
|
|
||||||
property int metadataWidth
|
property int metadataWidth
|
||||||
property bool fitsMetadata: (parent.width - width) > metadataWidth+4
|
property bool fitsMetadata: parent != null ? (parent.width - width) > metadataWidth+4 : false
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: img
|
id: img
|
||||||
|
|
||||||
visible: !mxcimage.loaded
|
visible: !mxcimage.loaded
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: url.replace("mxc://", "image://MxcImage/") + "?scale"
|
source: url != "" ? (url.replace("mxc://", "image://MxcImage/") + "?scale") : ""
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
smooth: true
|
smooth: true
|
||||||
|
@ -137,7 +137,7 @@ AbstractButton {
|
||||||
id: blurhash_
|
id: blurhash_
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: blurhash ? ("image://blurhash/" + blurhash) : ("image://colorimage/:/icons/icons/ui/image-failed.svg?" + Nheko.colors.buttonText)
|
source: blurhash ? ("image://blurhash/" + blurhash) : ("image://colorimage/:/icons/icons/ui/image-failed.svg?" + palette.buttonText)
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
sourceSize.width: parent.width * Screen.devicePixelRatio
|
sourceSize.width: parent.width * Screen.devicePixelRatio
|
||||||
|
@ -158,7 +158,7 @@ AbstractButton {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
implicitHeight: imgcaption.implicitHeight
|
implicitHeight: imgcaption.implicitHeight
|
||||||
anchors.bottom: overlay.bottom
|
anchors.bottom: overlay.bottom
|
||||||
color: Nheko.colors.window
|
color: palette.window
|
||||||
opacity: 0.75
|
opacity: 0.75
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +171,7 @@ AbstractButton {
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
// See this MSC: https://github.com/matrix-org/matrix-doc/pull/2530
|
// See this MSC: https://github.com/matrix-org/matrix-doc/pull/2530
|
||||||
text: filename ? filename : body
|
text: filename ? filename : body
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ Item {
|
||||||
required property bool isReply
|
required property bool isReply
|
||||||
property bool keepFullText: !isReply
|
property bool keepFullText: !isReply
|
||||||
property alias child: chooser.child
|
property alias child: chooser.child
|
||||||
implicitWidth: (chooser.child && chooser.child.implicitWidth) ? chooser.child.implicitWidth : 0
|
//implicitWidth: chooser.child?.implicitWidth ?? 0
|
||||||
required property double proportionalHeight
|
required property double proportionalHeight
|
||||||
required property int type
|
required property int type
|
||||||
required property string typeString
|
required property string typeString
|
||||||
|
@ -39,6 +39,8 @@ Item {
|
||||||
property bool fitsMetadata: (chooser.child && chooser.child.fitsMetadata) ? chooser.child.fitsMetadata : false
|
property bool fitsMetadata: (chooser.child && chooser.child.fitsMetadata) ? chooser.child.fitsMetadata : false
|
||||||
property int metadataWidth
|
property int metadataWidth
|
||||||
|
|
||||||
|
implicitWidth: chooser.child?.implicitWidth
|
||||||
|
|
||||||
height: chooser.child ? chooser.child.height : Nheko.paddingLarge
|
height: chooser.child ? chooser.child.height : Nheko.paddingLarge
|
||||||
|
|
||||||
DelegateChooser {
|
DelegateChooser {
|
||||||
|
@ -48,7 +50,7 @@ Item {
|
||||||
roleValue: type
|
roleValue: type
|
||||||
//anchors.fill: parent
|
//anchors.fill: parent
|
||||||
|
|
||||||
width: parent.width? parent.width: 0 // this should get rid of "cannot read property 'width' of null"
|
width: parent?.width ?? 0 // this should get rid of "cannot read property 'width' of null"
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.UnknownEvent
|
roleValue: MtxEvent.UnknownEvent
|
||||||
|
@ -78,7 +80,6 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
palette: Nheko.colors
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("Go to replacement room")
|
text: qsTr("Go to replacement room")
|
||||||
onClicked: room.joinReplacementRoom(eventId)
|
onClicked: room.joinReplacementRoom(eventId)
|
||||||
|
@ -149,7 +150,7 @@ Item {
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
formatted: TimelineManager.escapeEmoji(d.userName) + " " + d.formattedBody
|
formatted: TimelineManager.escapeEmoji(d.userName) + " " + d.formattedBody
|
||||||
color: TimelineManager.userColor(d.userId, Nheko.colors.base)
|
color: TimelineManager.userColor(d.userId, palette.base)
|
||||||
body: d.body
|
body: d.body
|
||||||
isOnlyEmoji: d.isOnlyEmoji
|
isOnlyEmoji: d.isOnlyEmoji
|
||||||
isReply: d.isReply
|
isReply: d.isReply
|
||||||
|
@ -281,6 +282,20 @@ Item {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MtxEvent.ServerAcl
|
||||||
|
|
||||||
|
NoticeMessage {
|
||||||
|
body: formatted
|
||||||
|
isOnlyEmoji: false
|
||||||
|
isReply: d.isReply
|
||||||
|
keepFullText: d.keepFullText
|
||||||
|
isStateEvent: d.isStateEvent
|
||||||
|
formatted: qsTr("%1 changed which servers are allowed in this room.").arg(d.userName)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MtxEvent.Name
|
roleValue: MtxEvent.Name
|
||||||
|
|
||||||
|
@ -603,7 +618,7 @@ Item {
|
||||||
roleValue: MtxEvent.Member
|
roleValue: MtxEvent.Member
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
width: parent.width
|
width: parent?.width ?? 100
|
||||||
|
|
||||||
NoticeMessage {
|
NoticeMessage {
|
||||||
body: formatted
|
body: formatted
|
||||||
|
@ -617,7 +632,6 @@ Item {
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
visible: d.relatedEventCacheBuster, room.showAcceptKnockButton(d.eventId)
|
visible: d.relatedEventCacheBuster, room.showAcceptKnockButton(d.eventId)
|
||||||
palette: Nheko.colors
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("Allow them in")
|
text: qsTr("Allow them in")
|
||||||
onClicked: room.acceptKnock(eventId)
|
onClicked: room.acceptKnock(eventId)
|
||||||
|
|
|
@ -9,7 +9,7 @@ import im.nheko 1.0
|
||||||
TextMessage {
|
TextMessage {
|
||||||
property bool isStateEvent
|
property bool isStateEvent
|
||||||
font.italic: true
|
font.italic: true
|
||||||
color: Nheko.colors.buttonText
|
color: palette.buttonText
|
||||||
font.pointSize: isStateEvent? 0.8*Settings.fontSize : Settings.fontSize
|
font.pointSize: isStateEvent? 0.8*Settings.fontSize : Settings.fontSize
|
||||||
horizontalAlignment: isStateEvent? Text.AlignHCenter : undefined
|
horizontalAlignment: isStateEvent? Text.AlignHCenter : undefined
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,14 +8,14 @@ import im.nheko 1.0
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
property bool isStateEvent
|
property bool isStateEvent
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
height: Math.round(fontMetrics.height * 1.4)
|
height: Math.round(fontMetrics.height * 1.4)
|
||||||
width: contentWidth * 1.2
|
width: contentWidth * 1.2
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
radius: parent.height / 2
|
radius: parent.height / 2
|
||||||
color: Nheko.colors.alternateBase
|
color: palette.alternateBase
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,5 +10,5 @@ MatrixText {
|
||||||
|
|
||||||
text: qsTr("unimplemented event: ") + typeString
|
text: qsTr("unimplemented event: ") + typeString
|
||||||
// width: parent.width
|
// width: parent.width
|
||||||
color: Nheko.inactiveColors.text
|
color: palette.inactive.text
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,11 @@
|
||||||
|
|
||||||
import "../"
|
import "../"
|
||||||
import "../ui/media"
|
import "../ui/media"
|
||||||
import QtMultimedia 5.15
|
import QtMultimedia
|
||||||
import QtQuick 2.15
|
import QtQuick
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts
|
||||||
import im.nheko 1.0
|
import im.nheko
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: content
|
id: content
|
||||||
|
@ -25,9 +25,9 @@ Item {
|
||||||
property double divisor: isReply ? 4 : 2
|
property double divisor: isReply ? 4 : 2
|
||||||
property int tempWidth: originalWidth < 1? 400: originalWidth
|
property int tempWidth: originalWidth < 1? 400: originalWidth
|
||||||
implicitWidth: type == MtxEvent.VideoMessage ? Math.round(tempWidth*Math.min((timelineView.height/divisor)/(tempWidth*proportionalHeight), 1)) : 500
|
implicitWidth: type == MtxEvent.VideoMessage ? Math.round(tempWidth*Math.min((timelineView.height/divisor)/(tempWidth*proportionalHeight), 1)) : 500
|
||||||
width: Math.min(parent.width, implicitWidth)
|
width: Math.min(parent?.width ?? implicitWidth, implicitWidth)
|
||||||
height: (type == MtxEvent.VideoMessage ? width*proportionalHeight : 80) + fileInfoLabel.height
|
height: (type == MtxEvent.VideoMessage ? width*proportionalHeight : 80) + fileInfoLabel.height
|
||||||
implicitHeight: height
|
//implicitHeight: height
|
||||||
|
|
||||||
property int metadataWidth
|
property int metadataWidth
|
||||||
property bool fitsMetadata: (parent.width - fileInfoLabel.width) > metadataWidth+4
|
property bool fitsMetadata: (parent.width - fileInfoLabel.width) > metadataWidth+4
|
||||||
|
@ -36,18 +36,18 @@ Item {
|
||||||
id: mxcmedia
|
id: mxcmedia
|
||||||
|
|
||||||
// TODO: Show error in overlay or so?
|
// TODO: Show error in overlay or so?
|
||||||
onError: console.log(error)
|
|
||||||
roomm: room
|
roomm: room
|
||||||
// desiredVolume is a float from 0.0 -> 1.0, MediaPlayer volume is an int from 0 to 100
|
audioOutput: AudioOutput {
|
||||||
// this value automatically gets clamped for us between these two values.
|
muted: mediaControls.muted
|
||||||
volume: mediaControls.desiredVolume * 100
|
volume: mediaControls.desiredVolume
|
||||||
muted: mediaControls.muted
|
}
|
||||||
|
videoOutput: videoOutput
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: videoContainer
|
id: videoContainer
|
||||||
|
|
||||||
color: type == MtxEvent.VideoMessage ? Nheko.colors.window : "transparent"
|
color: type == MtxEvent.VideoMessage ? palette.window : "transparent"
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height - fileInfoLabel.height
|
height: parent.height - fileInfoLabel.height
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ Item {
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: thumbnailUrl ? thumbnailUrl.replace("mxc://", "image://MxcImage/") + "?scale" : "image://colorimage/:/icons/icons/ui/video-file.svg?" + Nheko.colors.windowText
|
source: thumbnailUrl ? thumbnailUrl.replace("mxc://", "image://MxcImage/") + "?scale" : "image://colorimage/:/icons/icons/ui/video-file.svg?" + palette.windowText
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
|
|
||||||
|
@ -68,43 +68,40 @@ Item {
|
||||||
clip: true
|
clip: true
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
fillMode: VideoOutput.PreserveAspectFit
|
fillMode: VideoOutput.PreserveAspectFit
|
||||||
source: mxcmedia
|
|
||||||
flushMode: VideoOutput.FirstFrame
|
|
||||||
orientation: mxcmedia.orientation
|
orientation: mxcmedia.orientation
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
MediaControls {
|
||||||
|
id: mediaControls
|
||||||
|
|
||||||
MediaControls {
|
anchors.left: videoContainer.left
|
||||||
id: mediaControls
|
anchors.right: videoContainer.right
|
||||||
|
anchors.bottom: videoContainer.bottom
|
||||||
anchors.left: content.left
|
playingVideo: type == MtxEvent.VideoMessage
|
||||||
anchors.right: content.right
|
positionValue: mxcmedia.position
|
||||||
anchors.bottom: fileInfoLabel.top
|
duration: mediaLoaded ? mxcmedia.duration : content.duration
|
||||||
playingVideo: type == MtxEvent.VideoMessage
|
mediaLoaded: mxcmedia.loaded
|
||||||
positionValue: mxcmedia.position
|
mediaState: mxcmedia.playbackState
|
||||||
duration: mediaLoaded ? mxcmedia.duration : content.duration
|
onPositionChanged: mxcmedia.position = position
|
||||||
mediaLoaded: mxcmedia.loaded
|
onPlayPauseActivated: mxcmedia.playbackState == MediaPlayer.PlayingState ? mxcmedia.pause() : mxcmedia.play()
|
||||||
mediaState: mxcmedia.state
|
onLoadActivated: mxcmedia.eventId = eventId
|
||||||
onPositionChanged: mxcmedia.position = position
|
}
|
||||||
onPlayPauseActivated: mxcmedia.state == MediaPlayer.PlayingState ? mxcmedia.pause() : mxcmedia.play()
|
|
||||||
onLoadActivated: mxcmedia.eventId = eventId
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// information about file name and file size
|
// information about file name and file size
|
||||||
Label {
|
Label {
|
||||||
id: fileInfoLabel
|
id: fileInfoLabel
|
||||||
|
|
||||||
anchors.bottom: content.bottom
|
anchors.top: videoContainer.bottom
|
||||||
text: body + " [" + filesize + "]"
|
text: body + " [" + filesize + "]"
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: Nheko.colors.base
|
color: palette.base
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ Rectangle{
|
||||||
implicitWidth: redactedLayout.implicitWidth + 2 * Nheko.paddingMedium
|
implicitWidth: redactedLayout.implicitWidth + 2 * Nheko.paddingMedium
|
||||||
width: Math.min(parent.width,implicitWidth+1)
|
width: Math.min(parent.width,implicitWidth+1)
|
||||||
radius: fontMetrics.lineSpacing / 2 + 2 * Nheko.paddingSmall
|
radius: fontMetrics.lineSpacing / 2 + 2 * Nheko.paddingSmall
|
||||||
color: Nheko.colors.alternateBase
|
color: palette.alternateBase
|
||||||
property int metadataWidth
|
property int metadataWidth
|
||||||
property bool fitsMetadata: parent.width - redactedLayout.width > metadataWidth + 4
|
property bool fitsMetadata: parent.width - redactedLayout.width > metadataWidth + 4
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ Rectangle{
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||||
Layout.preferredWidth: fontMetrics.font.pixelSize
|
Layout.preferredWidth: fontMetrics.font.pixelSize
|
||||||
Layout.preferredHeight: fontMetrics.font.pixelSize
|
Layout.preferredHeight: fontMetrics.font.pixelSize
|
||||||
source: "image://colorimage/:/icons/icons/ui/delete.svg?" + Nheko.colors.text
|
source: "image://colorimage/:/icons/icons/ui/delete.svg?" + palette.text
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
id: redactedLabel
|
id: redactedLabel
|
||||||
|
@ -39,7 +39,7 @@ Rectangle{
|
||||||
property var redactedPair: room.formatRedactedEvent(eventId)
|
property var redactedPair: room.formatRedactedEvent(eventId)
|
||||||
text: redactedPair["first"]
|
text: redactedPair["first"]
|
||||||
wrapMode: Label.WordWrap
|
wrapMode: Label.WordWrap
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
|
|
||||||
ToolTip.text: redactedPair["second"]
|
ToolTip.text: redactedPair["second"]
|
||||||
ToolTip.visible: hh.hovered
|
ToolTip.visible: hh.hovered
|
||||||
|
|
|
@ -43,7 +43,7 @@ AbstractButton {
|
||||||
implicitHeight: replyContainer.height
|
implicitHeight: replyContainer.height
|
||||||
implicitWidth: visible? colorLine.width+Math.max(replyContainer.implicitWidth,userName_.fullTextWidth) : 0 // visible? seems to be causing issues
|
implicitWidth: visible? colorLine.width+Math.max(replyContainer.implicitWidth,userName_.fullTextWidth) : 0 // visible? seems to be causing issues
|
||||||
|
|
||||||
CursorShape {
|
NhekoCursorShape {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ AbstractButton {
|
||||||
anchors.top: replyContainer.top
|
anchors.top: replyContainer.top
|
||||||
anchors.bottom: replyContainer.bottom
|
anchors.bottom: replyContainer.bottom
|
||||||
width: 4
|
width: 4
|
||||||
color: TimelineManager.userColor(userId, Nheko.colors.base)
|
color: TimelineManager.userColor(userId, palette.base)
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
@ -135,8 +135,8 @@ AbstractButton {
|
||||||
|
|
||||||
z: -1
|
z: -1
|
||||||
anchors.fill: replyContainer
|
anchors.fill: replyContainer
|
||||||
property color userColor: TimelineManager.userColor(userId, Nheko.colors.base)
|
property color userColor: TimelineManager.userColor(userId, palette.base)
|
||||||
property color bgColor: Nheko.colors.base
|
property color bgColor: palette.base
|
||||||
color: Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.1))
|
color: Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,20 +14,19 @@ MatrixText {
|
||||||
required property string formatted
|
required property string formatted
|
||||||
property string copyText: selectedText ? getText(selectionStart, selectionEnd) : body
|
property string copyText: selectedText ? getText(selectionStart, selectionEnd) : body
|
||||||
property int metadataWidth
|
property int metadataWidth
|
||||||
property bool fitsMetadata: positionAt(width,height-4) == positionAt(width-metadataWidth-10, height-4)
|
property bool fitsMetadata: false //positionAt(width,height-4) == positionAt(width-metadataWidth-10, height-4)
|
||||||
|
|
||||||
// table border-collapse doesn't seem to work
|
// table border-collapse doesn't seem to work
|
||||||
text: "
|
text: "
|
||||||
<style type=\"text/css\">
|
<style type=\"text/css\">
|
||||||
a { color:" + Nheko.colors.link + ";}
|
code { background-color: " + palette.alternateBase + "; white-space: pre-wrap; }
|
||||||
code { background-color: " + Nheko.colors.alternateBase + "; white-space: pre-wrap; }
|
pre { background-color: " + palette.alternateBase + "; white-space: pre-wrap; }
|
||||||
pre { background-color: " + Nheko.colors.alternateBase + "; white-space: pre-wrap; }
|
|
||||||
table {
|
table {
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-color: " + Nheko.colors.text + ";
|
border-color: " + palette.text + ";
|
||||||
background-color: " + Nheko.colors.alternateBase + ";
|
background-color: " + palette.alternateBase + ";
|
||||||
}
|
}
|
||||||
table th,
|
table th,
|
||||||
table td {
|
table td {
|
||||||
|
@ -36,18 +35,18 @@ MatrixText {
|
||||||
blockquote { margin-left: 1em; }
|
blockquote { margin-left: 1em; }
|
||||||
" + (!Settings.mobileMode ? "span[data-mx-spoiler] {
|
" + (!Settings.mobileMode ? "span[data-mx-spoiler] {
|
||||||
color: transparent;
|
color: transparent;
|
||||||
background-color: " + Nheko.colors.text + ";
|
background-color: " + palette.text + ";
|
||||||
}" : "") + // TODO(Nico): Figure out how to support mobile
|
}" : "") + // TODO(Nico): Figure out how to support mobile
|
||||||
"</style>
|
"</style>
|
||||||
" + formatted.replace(/<del>/g, "<s>").replace(/<\/del>/g, "</s>").replace(/<strike>/g, "<s>").replace(/<\/strike>/g, "</s>")
|
" + formatted.replace(/<del>/g, "<s>").replace(/<\/del>/g, "</s>").replace(/<strike>/g, "<s>").replace(/<\/strike>/g, "</s>")
|
||||||
width: parent.width
|
width: parent?.width ?? 0
|
||||||
height: !keepFullText ? Math.round(Math.min(timelineView.height / 8, implicitHeight)) : implicitHeight
|
height: !keepFullText ? Math.round(Math.min(timelineView.height / 8, implicitHeight)) : implicitHeight
|
||||||
clip: !keepFullText
|
clip: !keepFullText
|
||||||
selectByMouse: !Settings.mobileMode && !isReply
|
selectByMouse: !Settings.mobileMode && !isReply
|
||||||
enabled: !Settings.mobileMode
|
enabled: !Settings.mobileMode
|
||||||
font.pointSize: (Settings.enlargeEmojiOnlyMessages && isOnlyEmoji > 0 && isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
|
font.pointSize: (Settings.enlargeEmojiOnlyMessages && isOnlyEmoji > 0 && isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
|
||||||
|
|
||||||
CursorShape {
|
NhekoCursorShape {
|
||||||
enabled: isReply
|
enabled: isReply
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
|
@ -15,8 +15,7 @@ ApplicationWindow {
|
||||||
onClosing: VerificationManager.removeVerificationFlow(flow)
|
onClosing: VerificationManager.removeVerificationFlow(flow)
|
||||||
title: stack.currentItem ? (stack.currentItem.title_ || "") : ""
|
title: stack.currentItem ? (stack.currentItem.title_ || "") : ""
|
||||||
modality: Qt.NonModal
|
modality: Qt.NonModal
|
||||||
palette: Nheko.colors
|
color: palette.window
|
||||||
color: Nheko.colors.window
|
|
||||||
//height: stack.currentItem.implicitHeight
|
//height: stack.currentItem.implicitHeight
|
||||||
minimumHeight: stack.currentItem.implicitHeight + 2 * Nheko.paddingLarge
|
minimumHeight: stack.currentItem.implicitHeight + 2 * Nheko.paddingLarge
|
||||||
height: stack.currentItem.implicitHeight + 2 * Nheko.paddingMedium
|
height: stack.currentItem.implicitHeight + 2 * Nheko.paddingMedium
|
||||||
|
@ -25,7 +24,7 @@ ApplicationWindow {
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: Nheko.colors.window
|
color: palette.window
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
text: qsTr("Please verify the following digits. You should see the same numbers on both sides. If they differ, please press 'They do not match!' to abort verification!")
|
text: qsTr("Please verify the following digits. You should see the same numbers on both sides. If they differ, please press 'They do not match!' to abort verification!")
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,19 +28,19 @@ ColumnLayout {
|
||||||
Label {
|
Label {
|
||||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||||
text: flow.sasList[0]
|
text: flow.sasList[0]
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||||
text: flow.sasList[1]
|
text: flow.sasList[1]
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||||
text: flow.sasList[2]
|
text: flow.sasList[2]
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
text: qsTr("Please verify the following emoji. You should see the same emoji on both sides. If they differ, please press 'They do not match!' to abort verification!")
|
text: qsTr("Please verify the following emoji. You should see the same emoji on both sides. If they differ, please press 'They do not match!' to abort verification!")
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,13 +373,13 @@ ColumnLayout {
|
||||||
text: col.emoji.emoji
|
text: col.emoji.emoji
|
||||||
font.pixelSize: Qt.application.font.pixelSize * 2
|
font.pixelSize: Qt.application.font.pixelSize * 2
|
||||||
font.family: Settings.emojiFont
|
font.family: Settings.emojiFont
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||||
text: col.emoji.description
|
text: col.emoji.description
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -396,7 +396,7 @@ ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
text: qsTr("The displayed emoji might look different in different clients if a different font is used. Similarly they might be translated into different languages. Nonetheless they should depict one of 64 different objects or animals. For example a lion and a cat are different, but a cat is the same even if one client just shows a cat face, while another client shows a full cat body.")
|
text: qsTr("The displayed emoji might look different in different clients if a different font is used. Similarly they might be translated into different languages. Nonetheless they should depict one of 64 different objects or animals. For example a lion and a cat are different, but a cat is the same even if one client just shows a cat face, while another client shows a full cat body.")
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ ColumnLayout {
|
||||||
return qsTr("Unknown verification error.");
|
return qsTr("Unknown verification error.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ ColumnLayout {
|
||||||
return qsTr("Your device (%1) has requested to be verified.").arg(flow.deviceId);
|
return qsTr("Your device (%1) has requested to be verified.").arg(flow.deviceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
text: qsTr("Verification successful! Both sides verified their devices!")
|
text: qsTr("Verification successful! Both sides verified their devices!")
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,14 +30,14 @@ ColumnLayout {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillHeight: true; }
|
Item { Layout.fillHeight: true; }
|
||||||
Spinner {
|
Spinner {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
foreground: Nheko.colors.mid
|
foreground: palette.mid
|
||||||
}
|
}
|
||||||
Item { Layout.fillHeight: true; }
|
Item { Layout.fillHeight: true; }
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ ApplicationWindow {
|
||||||
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
|
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: false
|
Layout.fillHeight: false
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
Layout.bottomMargin: Nheko.paddingMedium
|
Layout.bottomMargin: Nheko.paddingMedium
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,10 +53,6 @@ ApplicationWindow {
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
ScrollHelper {
|
|
||||||
flickable: parent
|
|
||||||
anchors.fill: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
model: editingModel
|
model: editingModel
|
||||||
spacing: 4
|
spacing: 4
|
||||||
|
@ -69,7 +65,7 @@ ApplicationWindow {
|
||||||
Text {
|
Text {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: model.name
|
text: model.name
|
||||||
color: model.isPublished ? Nheko.colors.text : Nheko.theme.error
|
color: model.isPublished ? palette.text : Nheko.theme.error
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,8 +74,8 @@ ApplicationWindow {
|
||||||
Layout.margins: 2
|
Layout.margins: 2
|
||||||
image: ":/icons/icons/ui/star.svg"
|
image: ":/icons/icons/ui/star.svg"
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
buttonTextColor: model.isCanonical ? Nheko.colors.highlight : Nheko.colors.text
|
buttonTextColor: model.isCanonical ? palette.highlight : palette.text
|
||||||
highlightColor: editingModel.canAdvertize ? Nheko.colors.highlight : buttonTextColor
|
highlightColor: editingModel.canAdvertize ? palette.highlight : buttonTextColor
|
||||||
|
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
ToolTip.text: model.isCanonical ? qsTr("Primary alias") : qsTr("Make primary alias")
|
ToolTip.text: model.isCanonical ? qsTr("Primary alias") : qsTr("Make primary alias")
|
||||||
|
@ -92,8 +88,8 @@ ApplicationWindow {
|
||||||
Layout.margins: 2
|
Layout.margins: 2
|
||||||
image: ":/icons/icons/ui/building-shop.svg"
|
image: ":/icons/icons/ui/building-shop.svg"
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
buttonTextColor: model.isAdvertized ? Nheko.colors.highlight : Nheko.colors.text
|
buttonTextColor: model.isAdvertized ? palette.highlight : palette.text
|
||||||
highlightColor: editingModel.canAdvertize ? Nheko.colors.highlight : buttonTextColor
|
highlightColor: editingModel.canAdvertize ? palette.highlight : buttonTextColor
|
||||||
|
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
ToolTip.text: qsTr("Advertise as an alias in this room")
|
ToolTip.text: qsTr("Advertise as an alias in this room")
|
||||||
|
@ -106,7 +102,7 @@ ApplicationWindow {
|
||||||
Layout.margins: 2
|
Layout.margins: 2
|
||||||
image: ":/icons/icons/ui/room-directory.svg"
|
image: ":/icons/icons/ui/room-directory.svg"
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
buttonTextColor: model.isPublished ? Nheko.colors.highlight : Nheko.colors.text
|
buttonTextColor: model.isPublished ? palette.highlight : palette.text
|
||||||
|
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
ToolTip.text: qsTr("Publish in room directory")
|
ToolTip.text: qsTr("Publish in room directory")
|
||||||
|
@ -139,7 +135,7 @@ ApplicationWindow {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
font.pixelSize: fontMetrics.font.pixelSize
|
font.pixelSize: fontMetrics.font.pixelSize
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
placeholderText: qsTr("#new-alias:server.tld")
|
placeholderText: qsTr("#new-alias:server.tld")
|
||||||
|
|
||||||
Component.onCompleted: forceActiveFocus()
|
Component.onCompleted: forceActiveFocus()
|
||||||
|
|
|
@ -20,8 +20,7 @@ ApplicationWindow {
|
||||||
minimumHeight: 450
|
minimumHeight: 450
|
||||||
width: 450
|
width: 450
|
||||||
height: 680
|
height: 680
|
||||||
palette: Nheko.colors
|
color: palette.window
|
||||||
color: Nheko.colors.window
|
|
||||||
modality: Qt.NonModal
|
modality: Qt.NonModal
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
title: qsTr("Allowed rooms settings")
|
title: qsTr("Allowed rooms settings")
|
||||||
|
@ -42,7 +41,7 @@ ApplicationWindow {
|
||||||
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
|
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: false
|
Layout.fillHeight: false
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
Layout.bottomMargin: Nheko.paddingMedium
|
Layout.bottomMargin: Nheko.paddingMedium
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,10 +53,6 @@ ApplicationWindow {
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
ScrollHelper {
|
|
||||||
flickable: parent
|
|
||||||
anchors.fill: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
model: roomSettings.allowedRoomsModel
|
model: roomSettings.allowedRoomsModel
|
||||||
spacing: 4
|
spacing: 4
|
||||||
|
@ -72,14 +67,14 @@ ApplicationWindow {
|
||||||
Text {
|
Text {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: model.name
|
text: model.name
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: model.isParent ? qsTr("Parent community") : qsTr("Other room")
|
text: model.isParent ? qsTr("Parent community") : qsTr("Other room")
|
||||||
color: Nheko.colors.buttonText
|
color: palette.buttonText
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,7 +117,7 @@ ApplicationWindow {
|
||||||
|
|
||||||
placeholderText: qsTr("Enter additional rooms not in the list yet...")
|
placeholderText: qsTr("Enter additional rooms not in the list yet...")
|
||||||
|
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
onTextEdited: {
|
onTextEdited: {
|
||||||
roomCompleter.completer.searchString = text;
|
roomCompleter.completer.searchString = text;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,7 @@ ApplicationWindow {
|
||||||
title: summary.isSpace ? qsTr("Confirm community join") : qsTr("Confirm room join")
|
title: summary.isSpace ? qsTr("Confirm community join") : qsTr("Confirm room join")
|
||||||
modality: Qt.WindowModal
|
modality: Qt.WindowModal
|
||||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||||
palette: Nheko.colors
|
color: palette.window
|
||||||
color: Nheko.colors.window
|
|
||||||
width: 350
|
width: 350
|
||||||
height: content.implicitHeight + Nheko.paddingLarge + footer.implicitHeight
|
height: content.implicitHeight + Nheko.paddingLarge + footer.implicitHeight
|
||||||
|
|
||||||
|
@ -48,7 +47,7 @@ ApplicationWindow {
|
||||||
Spinner {
|
Spinner {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
visible: !summary.isLoaded
|
visible: !summary.isLoaded
|
||||||
foreground: Nheko.colors.mid
|
foreground: palette.mid
|
||||||
running: !summary.isLoaded
|
running: !summary.isLoaded
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +56,7 @@ ApplicationWindow {
|
||||||
textFormat: TextEdit.RichText
|
textFormat: TextEdit.RichText
|
||||||
text: summary.roomName
|
text: summary.roomName
|
||||||
font.pixelSize: fontMetrics.font.pixelSize * 2
|
font.pixelSize: fontMetrics.font.pixelSize * 2
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
@ -70,7 +69,7 @@ ApplicationWindow {
|
||||||
textFormat: TextEdit.RichText
|
textFormat: TextEdit.RichText
|
||||||
text: summary.roomid
|
text: summary.roomid
|
||||||
font.pixelSize: fontMetrics.font.pixelSize * 0.8
|
font.pixelSize: fontMetrics.font.pixelSize * 0.8
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
@ -96,7 +95,7 @@ ApplicationWindow {
|
||||||
readOnly: true
|
readOnly: true
|
||||||
textFormat: TextEdit.RichText
|
textFormat: TextEdit.RichText
|
||||||
text: summary.roomTopic
|
text: summary.roomTopic
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
@ -109,7 +108,7 @@ ApplicationWindow {
|
||||||
id: promptLabel
|
id: promptLabel
|
||||||
|
|
||||||
text: summary.isKnockOnly ? qsTr("This room can't be joined directly. You can, however, knock on the room and room members can accept or decline this join request. You can additionally provide a reason for them to let you in below:") : qsTr("Do you want to join this room? You can optionally add a reason below:")
|
text: summary.isKnockOnly ? qsTr("This room can't be joined directly. You can, however, knock on the room and room members can accept or decline this join request. You can additionally provide a reason for them to let you in below:") : qsTr("Do you want to join this room? You can optionally add a reason below:")
|
||||||
color: Nheko.colors.text
|
color: palette.text
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue