diff --git a/.ci/macos/Brewfile b/.ci/macos/Brewfile
index 0674eb61..717447fc 100644
--- a/.ci/macos/Brewfile
+++ b/.ci/macos/Brewfile
@@ -6,7 +6,7 @@ brew "clang-format"
brew "cmake"
brew "ninja"
brew "openssl"
-brew "qt5"
+brew "qt6"
brew "nlohmann_json"
brew "gstreamer"
brew "qtkeychain"
diff --git a/.ci/macos/build.sh b/.ci/macos/build.sh
index 40ab2571..aa515186 100755
--- a/.ci/macos/build.sh
+++ b/.ci/macos/build.sh
@@ -6,27 +6,32 @@ set -ue
#TAG=$(git tag -l --points-at HEAD)
# Add Qt binaries to path
-PATH="$(brew --prefix qt5):${PATH}"
+PATH="$(brew --prefix qt6)/bin/:${PATH}"
export PATH
-CMAKE_PREFIX_PATH="$(brew --prefix qt5)"
+CMAKE_PREFIX_PATH="$(brew --prefix qt6)"
export CMAKE_PREFIX_PATH
cmake -GNinja -S. -Bbuild \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
- -DCMAKE_INSTALL_PREFIX=.deps/usr \
+ -DCMAKE_INSTALL_PREFIX="nheko.temp" \
-DHUNTER_ROOT="../.hunter" \
-DHUNTER_ENABLED=ON -DBUILD_SHARED_LIBS=OFF \
-DCMAKE_BUILD_TYPE=RelWithDebInfo -DHUNTER_CONFIGURATION_TYPES=RelWithDebInfo \
-DUSE_BUNDLED_OPENSSL=ON \
-DCI_BUILD=ON
cmake --build build
+cmake --install build
( cd build
git clone https://github.com/Nheko-Reborn/qt-jdenticon.git
( cd qt-jdenticon
qmake
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
diff --git a/.ci/macos/notarize.sh b/.ci/macos/notarize.sh
index 345f4828..33a6da50 100755
--- a/.ci/macos/notarize.sh
+++ b/.ci/macos/notarize.sh
@@ -6,7 +6,7 @@ set -u
# https://forum.qt.io/topic/96652/how-to-notarize-qt-application-on-macos/18
# Add Qt binaries to path
-PATH="/usr/local/opt/qt@5/bin/:${PATH}"
+PATH="/usr/local/opt/qt@6/bin/:${PATH}"
export PATH
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
# 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.
- ( cd build || exit
- unzip nheko.zip
- )
+ unzip nheko.zip
fi
-if [ ! -d "build/nheko.app" ]; then
+if [ ! -d "nheko.app" ]; then
echo "nheko.app is missing, you did something wrong!"
exit 1
fi
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
echo "[INFO] Signing $fname"
codesign --force --timestamp --options=runtime --sign "${APPLE_DEV_IDENTITY}" "$fname"
fi
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_STATUS_LOG=$(mktemp /tmp/notarize-status.XXXXXX)
@@ -100,4 +98,4 @@ if [ -n "$VERSION" ]; then
mv nheko.dmg "nheko-${VERSION}-${PLAT}.dmg"
mkdir -p artifacts
cp "nheko-${VERSION}-${PLAT}.dmg" artifacts/
-fi
\ No newline at end of file
+fi
diff --git a/.ci/macos/settings.json b/.ci/macos/settings.json
index d156a3b6..e1410b52 100644
--- a/.ci/macos/settings.json
+++ b/.ci/macos/settings.json
@@ -3,7 +3,7 @@
"compression-level": 9,
"contents": [
{
- "path": "./build/Nheko.app",
+ "path": "./nheko.app",
"type": "file",
"x": 140,
"y": 120
diff --git a/.cirrus.yml b/.cirrus.yml
index 11de4d1a..8618b7ee 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -13,9 +13,9 @@ task:
- export PATH="$(brew --prefix qt5)/bin/:${PATH}"
- ./.ci/macos/build.sh
zip_script:
- - ditto -c -k --sequesterRsrc --keepParent build/nheko.app build/nheko.zip
+ - ditto -c -k --sequesterRsrc --keepParent nheko.app nheko.zip
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
binaries_artifacts:
- path: build/nheko.zip
+ path: nheko.zip
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 3dfc7f59..ffa436d3 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -18,14 +18,14 @@ build-clazy:
TRAVIS_OS_NAME: linux
before_script:
- 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"
script:
- export PATH="/usr/lib/ccache:${PATH}"
- export CMAKE_BUILD_PARALLEL_LEVEL=$(cat /proc/cpuinfo | awk '/^processor/{print $3}' | wc -l)
- cmake -GNinja -H. -Bbuild
-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
-DCMAKE_BUILD_TYPE=Release
-DCI_BUILD=ON -DFETCHCONTENT_QUIET=OFF -DCMAKE_CXX_COMPILER=clazy
@@ -37,7 +37,8 @@ build-clazy:
paths:
- .ccache
-build-gcc11:
+# disabled until I find a qt6.5 ppa
+.build-gcc11:
stage: build
image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/ubuntu:22.04
tags: [docker]
@@ -50,7 +51,7 @@ build-gcc11:
libssl-dev libqt5multimedia5-plugins libqt5multimediagsttools5 libqt5multimediaquick5 libqt5svg5-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
- 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
- apt-get -y install wget
- /usr/sbin/update-ccache-symlinks
@@ -107,16 +108,15 @@ build-tw:
"zlib-devel"
"libQt5PlatformHeaders-devel"
"cmake(re2)"
- "cmake(Qt5Concurrent)"
- "cmake(Qt5Core)"
- "cmake(Qt5DBus)"
- "cmake(Qt5Keychain)"
- "cmake(Qt5LinguistTools)"
- "cmake(Qt5Multimedia)"
- "cmake(Qt5Network)"
- "cmake(Qt5QuickControls2)"
- "cmake(Qt5Svg)"
- "cmake(Qt5Widgets)"
+ "cmake(Qt6Core)"
+ "cmake(Qt6DBus)"
+ "cmake(Qt6Keychain)"
+ "cmake(Qt6LinguistTools)"
+ "cmake(Qt6Multimedia)"
+ "cmake(Qt6QuickControls2)"
+ "cmake(Qt6Svg)"
+ "cmake(Qt6Widgets)"
+ "cmake(Qt6Gui)"
"pkgconfig(libcurl)"
"pkgconfig(libevent)"
"pkgconfig(gstreamer-webrtc-1.0)"
@@ -155,7 +155,7 @@ build-macos:
- if : '$CI_PIPELINE_TRIGGERED == null'
artifacts:
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
expose_as: 'macos-app'
public: false
@@ -200,7 +200,10 @@ build-flatpak:
- docker-${ARCH}
parallel:
matrix:
- - ARCH: [amd64, arm64]
+ - ARCH: amd64
+ JOBS: 0
+ - ARCH: arm64
+ JOBS: 3
before_script:
- 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
@@ -213,9 +216,9 @@ build-flatpak:
- mkdir -p build-flatpak
- cd build-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"
- - 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:
- 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
@@ -233,7 +236,8 @@ build-flatpak:
paths: ['build-flatpak/nheko-${ARCH}.flatpak']
name: flatpak-${CI_COMMIT_REF_NAME}-${VERSION}-${ARCH}
-appimage-amd64:
+# disabled until I find a qt6.5 ppa for Ubuntu
+.appimage-amd64:
stage: build
image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/ubuntu:22.04
tags: [docker]
@@ -249,7 +253,7 @@ appimage-amd64:
libssl-dev libqt5multimedia5-plugins libqt5multimediagsttools5 libqt5multimediaquick5 libqt5svg5-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
- 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
- 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:
- if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'
dependencies:
- - appimage-amd64
+ #- appimage-amd64 <- disabled because of missing packages
- build-flatpak
- codesign-macos
before_script:
diff --git a/.qmlformat.ini b/.qmlformat.ini
new file mode 100644
index 00000000..c136c23b
--- /dev/null
+++ b/.qmlformat.ini
@@ -0,0 +1,7 @@
+[General]
+FunctionsSpacing=
+IndentWidth=4
+NewlineType=native
+NormalizeOrder=true
+ObjectsSpacing=
+UseTabs=false
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b7c02ffb..1d43cfe6 100644
--- a/CMakeLists.txt
+++ b/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(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_LMDB "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"
${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)
endif()
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)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
@@ -239,16 +243,17 @@ endif()
#
# Discover Qt dependencies.
#
-find_package(Qt5 5.15 COMPONENTS Core Widgets LinguistTools Concurrent Svg Multimedia Qml QuickControls2 QuickWidgets REQUIRED)
-find_package(Qt5QuickCompiler)
-find_package(Qt5DBus)
+find_package(Qt6 6.5 COMPONENTS Core Widgets Gui LinguistTools Svg Multimedia Qml QuickControls2 REQUIRED)
+#find_package(Qt6QuickCompiler)
+find_package(Qt6DBus)
if (USE_BUNDLED_QTKEYCHAIN)
include(FetchContent)
+ set(BUILD_WITH_QT6 ON)
FetchContent_Declare(
- qt5keychain
+ qt6keychain
GIT_REPOSITORY https://github.com/frankosterfeld/qtkeychain.git
- GIT_TAG v0.13.1
+ GIT_TAG v0.14.0
)
if (BUILD_SHARED_LIBS)
set(QTKEYCHAIN_STATIC OFF CACHE INTERNAL "")
@@ -256,21 +261,17 @@ if (USE_BUNDLED_QTKEYCHAIN)
set(QTKEYCHAIN_STATIC ON CACHE INTERNAL "")
endif()
set(BUILD_TEST_APPLICATION OFF CACHE INTERNAL "")
- FetchContent_MakeAvailable(qt5keychain)
+ FetchContent_MakeAvailable(qt6keychain)
else()
- find_package(Qt5Keychain REQUIRED)
+ find_package(Qt6Keychain REQUIRED)
endif()
-if (APPLE)
- find_package(Qt5MacExtras REQUIRED)
-endif(APPLE)
-
-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!")
+if (Qt6Widgets_FOUND)
+ if (Qt6Widgets_VERSION VERSION_LESS 6.5.0)
+ message(STATUS "Qt version ${Qt6Widgets_VERSION}")
+ message(WARNING "Minimum supported Qt6 version is 6.5!")
endif()
-endif(Qt5Widgets_FOUND)
+endif(Qt6Widgets_FOUND)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
if(NOT MSVC)
@@ -386,6 +387,8 @@ set(SRC_FILES
# UI components
src/ui/HiddenEvents.cpp
src/ui/HiddenEvents.h
+ src/ui/EventExpiry.cpp
+ src/ui/EventExpiry.h
src/ui/MxcAnimatedImage.cpp
src/ui/MxcAnimatedImage.h
src/ui/MxcMediaProxy.cpp
@@ -394,8 +397,6 @@ set(SRC_FILES
src/ui/NhekoCursorShape.h
src/ui/NhekoDropArea.cpp
src/ui/NhekoDropArea.h
- src/ui/NhekoEventObserver.cpp
- src/ui/NhekoEventObserver.h
src/ui/NhekoGlobalObject.cpp
src/ui/NhekoGlobalObject.h
src/ui/RoomSettings.cpp
@@ -498,8 +499,6 @@ set(SRC_FILES
src/SingleImagePackModel.h
src/TrayIcon.cpp
src/TrayIcon.h
- src/UserDirectoryModel.cpp
- src/UserDirectoryModel.h
src/UserSettingsPage.cpp
src/UserSettingsPage.h
src/UsersModel.cpp
@@ -602,7 +601,7 @@ if(USE_BUNDLED_MTXCLIENT)
FetchContent_Declare(
MatrixClient
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_TESTS OFF CACHE INTERNAL "")
@@ -614,9 +613,10 @@ endif()
if (VOIP)
include(FindPkgConfig)
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)
- pkg_check_modules(XCB REQUIRED IMPORTED_TARGET xcb xcb-ewmh)
- endif()
+endif()
+
+if (X11 AND NOT WIN32 AND NOT APPLE AND NOT HAIKU)
+ pkg_check_modules(XCB REQUIRED IMPORTED_TARGET xcb xcb-ewmh)
endif()
# single instance functionality
@@ -631,10 +631,13 @@ if (NOT APPLE AND NOT WIN32)
endif()
#
-# Bundle translations.
+# Bundle resources
#
-include(Translations)
-set(TRANSLATION_DEPS ${LANG_QRC} ${QRC} ${QM_SRC})
+if(Qt6QuickCompiler_FOUND AND COMPILE_QML)
+ qtquick_compiler_add_resources(QRC resources/res.qrc)
+else()
+ qt_add_resources(QRC resources/res.qrc)
+endif()
if (APPLE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Foundation -framework Cocoa -framework UserNotifications")
@@ -666,7 +669,7 @@ endif ()
set(NHEKO_DEPS
${SRC_FILES}
- ${TRANSLATION_DEPS}
+ ${QRC}
${META_FILES_TO_INCLUDE})
if(ASAN)
@@ -674,10 +677,10 @@ if(ASAN)
endif()
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)
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")
set_target_properties(nheko PROPERTIES ENABLE_EXPORTS ON)
@@ -689,22 +692,150 @@ set_target_properties(nheko
CMAKE_SKIP_INSTALL_RPATH TRUE
AUTOMOC ON)
-if(APPLE)
- target_link_libraries (nheko PRIVATE Qt5::MacExtras)
-elseif(WIN32)
+#
+# Bundle translations
+#
+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_link_libraries (nheko PRIVATE ${NTDLIB} Qt5::WinMain)
if(MSVC)
target_compile_options(nheko PUBLIC "/Zc:__cplusplus")
endif()
else()
- target_link_libraries (nheko PRIVATE Qt5::DBus)
+ target_link_libraries (nheko PRIVATE Qt6::DBus)
if (FLATPAK)
target_compile_definitions(nheko PRIVATE NHEKO_FLATPAK)
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)
target_include_directories(nheko PRIVATE third_party/cpp-httplib-0.5.12)
@@ -729,7 +860,7 @@ endif()
# Fixup bundled keychain include dirs
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()
if (NOT JSON_ImplicitConversions)
@@ -744,14 +875,13 @@ target_link_libraries(nheko PRIVATE
MatrixClient::MatrixClient
cmark::cmark
spdlog::spdlog
- Qt5::Widgets
- Qt5::Svg
- Qt5::Concurrent
- Qt5::Multimedia
- Qt5::Qml
- Qt5::QuickControls2
- Qt5::QuickWidgets
- qt5keychain
+ Qt::Widgets
+ Qt::Svg
+ Qt::Gui
+ Qt::Multimedia
+ Qt::Qml
+ Qt::QuickControls2
+ qt6keychain
nlohmann_json::nlohmann_json
lmdbxx::lmdbxx
liblmdb::lmdb
@@ -768,10 +898,10 @@ endif()
if (TARGET PkgConfig::GSTREAMER)
target_link_libraries(nheko PRIVATE PkgConfig::GSTREAMER)
target_compile_definitions(nheko PRIVATE GSTREAMER_AVAILABLE)
- if (TARGET PkgConfig::XCB)
- target_link_libraries(nheko PRIVATE PkgConfig::XCB)
- target_compile_definitions(nheko PRIVATE XCB_AVAILABLE)
- endif()
+endif()
+if (TARGET PkgConfig::XCB)
+ target_link_libraries(nheko PRIVATE PkgConfig::XCB)
+ target_compile_definitions(nheko PRIVATE XCB_AVAILABLE)
endif()
if(MSVC)
@@ -797,9 +927,23 @@ if(MAN)
add_subdirectory(man)
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(FLATPAK)
- set(APPID "io.github.NhekoReborn.Nheko")
+ set(APPID "im.nheko.Nheko")
set_target_properties(nheko PROPERTIES OUTPUT_NAME "${APPID}")
else()
set(APPID "nheko")
diff --git a/README.md b/README.md
index fc20e0c9..73f8587d 100644
--- a/README.md
+++ b/README.md
@@ -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)
[![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/)
-
+
[![#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/)
@@ -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
- Video call support (optional): compositor, opengl, qmlgl, rtp, vpx
- [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`.)
- A compiler that supports C++ 20:
- Clang 16 (Only clazy 16 is tested in CI)
diff --git a/appveyor.yml b/appveyor.yml
index 4a0b1eec..49bb1e51 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -22,7 +22,7 @@ build:
verbosity: minimal
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%
build_script:
diff --git a/cmake/Translations.cmake b/cmake/Translations.cmake
deleted file mode 100644
index 887697a8..00000000
--- a/cmake/Translations.cmake
+++ /dev/null
@@ -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} "\n \n")
- foreach(_lang ${QM_SRC})
- get_filename_component(_filename ${_lang} NAME)
- file(APPEND ${_qrc} " ${_filename}\n")
- endforeach(_lang)
- file(APPEND ${_qrc} " \n\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()
diff --git a/io.github.NhekoReborn.Nheko.yaml b/im.nheko.Nheko.yaml
similarity index 77%
rename from io.github.NhekoReborn.Nheko.yaml
rename to im.nheko.Nheko.yaml
index d45dba3d..4fa8ccfb 100644
--- a/io.github.NhekoReborn.Nheko.yaml
+++ b/im.nheko.Nheko.yaml
@@ -1,7 +1,7 @@
-id: io.github.NhekoReborn.Nheko
-command: io.github.NhekoReborn.Nheko
+id: im.nheko.Nheko
+command: im.nheko.Nheko
runtime: org.kde.Platform
-runtime-version: '5.15-22.08'
+runtime-version: '6.5'
sdk: org.kde.Sdk
finish-args:
- --device=dri
@@ -44,9 +44,9 @@ cleanup:
modules:
- name: lmdb
sources:
- - sha256: f3927859882eb608868c8c31586bb7eb84562a40a6bf5cc3e13b6b564641ea28
+ - sha256: 8c5a93ac3cc97427c54571ad5a6140b7469389d01e6d2f43df39f96d3a4ccef7
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:
- prefix=/app
no-autogen: true
@@ -109,45 +109,46 @@ modules:
tag: 0.20.4
type: git
url: https://gitlab.gnome.org/GNOME/libsecret.git
- - config-opts:
- - -DCMAKE_BUILD_TYPE=Release
- - -DAVIF_CODEC_AOM=ON
- #- -DBUILD_SHARED_LIBS=OFF
- buildsystem: cmake-ninja
- name: libavif
- sources:
- - sha256: 66e82854ceb84a3e542bc140a343bc90e56c68f3ecb4fff63e636c136ed9a05e
- type: archive
- url: https://github.com/AOMediaCodec/libavif/archive/refs/tags/v0.10.1.tar.gz
- - config-opts:
- - -DCMAKE_BUILD_TYPE=Release
- - -DWITH_EXAMPLES=OFF
- #- -DBUILD_SHARED_LIBS=OFF
- buildsystem: cmake-ninja
- name: libheif
- sources:
- - sha256: e1ac2abb354fdc8ccdca71363ebad7503ad731c84022cf460837f0839e171718
- type: archive
- url: https://github.com/strukturag/libheif/releases/download/v1.12.0/libheif-1.12.0.tar.gz
- - config-opts:
- - -DCMAKE_BUILD_TYPE=Release
- - -DKIMAGEFORMATS_HEIF=ON
- buildsystem: cmake-ninja
- name: KImageFormats
- sources:
- - commit: ae6b724824fc2fdf71d50dc7ae0052ad1551b25a
- tag: v5.93.0
- type: git
- url: https://invent.kde.org/frameworks/kimageformats.git
+ #- config-opts:
+ # - -DCMAKE_BUILD_TYPE=Release
+ # - -DAVIF_CODEC_AOM=ON
+ # #- -DBUILD_SHARED_LIBS=OFF
+ # buildsystem: cmake-ninja
+ # name: libavif
+ # sources:
+ # - sha256: 66e82854ceb84a3e542bc140a343bc90e56c68f3ecb4fff63e636c136ed9a05e
+ # type: archive
+ # url: https://github.com/AOMediaCodec/libavif/archive/refs/tags/v0.10.1.tar.gz
+ #- config-opts:
+ # - -DCMAKE_BUILD_TYPE=Release
+ # - -DWITH_EXAMPLES=OFF
+ # #- -DBUILD_SHARED_LIBS=OFF
+ # buildsystem: cmake-ninja
+ # name: libheif
+ # sources:
+ # - sha256: e1ac2abb354fdc8ccdca71363ebad7503ad731c84022cf460837f0839e171718
+ # type: archive
+ # url: https://github.com/strukturag/libheif/releases/download/v1.12.0/libheif-1.12.0.tar.gz
+ #- config-opts:
+ # - -DCMAKE_BUILD_TYPE=Release
+ # - -DKIMAGEFORMATS_HEIF=ON
+ # buildsystem: cmake-ninja
+ # name: KImageFormats
+ # sources:
+ # - commit: ae6b724824fc2fdf71d50dc7ae0052ad1551b25a
+ # tag: v5.93.0
+ # type: git
+ # url: https://invent.kde.org/frameworks/kimageformats.git
- config-opts:
- -DCMAKE_BUILD_TYPE=Release
- -DBUILD_TEST_APPLICATION=OFF
- -DQTKEYCHAIN_STATIC=ON
+ - -DBUILD_WITH_QT6=ON
buildsystem: cmake-ninja
name: QtKeychain
sources:
- - commit: f59ac26be709fd2d8d7a062fab1cf1e67a93806c
- tag: v0.13.1
+ - commit: 69f993c47efed7e557d79a30a367014d9a27d809
+ tag: 0.14.1
type: git
url: https://github.com/frankosterfeld/qtkeychain.git
- config-opts:
@@ -170,15 +171,15 @@ modules:
- buildsystem: meson
name: gstreamer
sources:
- - commit: f7806a854aad960eae3288db4a67a574f92428fe
- tag: 1.20.5
+ - commit: ecd471f5ea4645102b206a43d863f0f0fe7d04ec
+ tag: 1.22.3
type: git
url: https://gitlab.freedesktop.org/gstreamer/gstreamer.git
config-opts:
- --auto-features=disabled
- -Dgood=enabled
- - -Dgst-plugins-good:qt5=enabled
- - -Dqt5=enabled
+ - -Dgst-plugins-good:qt6=enabled
+ #- -Dqt6=enabled <- not available on 1.22
- -Dbase=enabled
- -Dgst-plugins-base:gl=enabled
- -Dgst-plugins-base:gl_platform=glx,egl
@@ -213,7 +214,7 @@ modules:
buildsystem: cmake-ninja
name: mtxclient
sources:
- - commit: e136bc27b28d3bb5683735eb5a65d6ef2534ca3a
+ - commit: 0a4cc9421a97bea81a8921f3f5e040f0a34278fc
#tag: v0.9.2
type: git
url: https://github.com/Nheko-Reborn/mtxclient.git
diff --git a/nheko-nightly.flatpakref b/nheko-nightly.flatpakref
index 74e47ecd..484b5de0 100644
--- a/nheko-nightly.flatpakref
+++ b/nheko-nightly.flatpakref
@@ -1,6 +1,6 @@
[Flatpak Ref]
Title=Nheko Nightly
-Name=io.github.NhekoReborn.Nheko
+Name=im.nheko.Nheko
Branch=master
Url=https://flatpak.neko.dev/repo/nightly
SuggestRemoteName=nheko-nightlies
diff --git a/resources/langs/nheko_ca.ts b/resources/langs/nheko_ca.ts
index 035978f3..8de8ee24 100644
--- a/resources/langs/nheko_ca.ts
+++ b/resources/langs/nheko_ca.ts
@@ -1390,11 +1390,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
-
-
diff --git a/resources/langs/nheko_cs.ts b/resources/langs/nheko_cs.ts
index 9c4af145..87ea404f 100644
--- a/resources/langs/nheko_cs.ts
+++ b/resources/langs/nheko_cs.ts
@@ -1392,11 +1392,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
-
-
diff --git a/resources/langs/nheko_de.ts b/resources/langs/nheko_de.ts
index 24cd3f7b..d77eacf1 100644
--- a/resources/langs/nheko_de.ts
+++ b/resources/langs/nheko_de.ts
@@ -43,12 +43,12 @@
- Konnte den Alias %1 nicht depublizieren: %2
+ Konnte die Raumadresse %1 nicht entfernen: %2
- Konnte die Aliasse nicht aktualisieren: %1
+ Konnte die Raumadressen nicht aktualisieren: %1
@@ -56,12 +56,12 @@
- Aliasse für %1
+ Aliase für %1
- Aliasse dieses Raumes. Normalerweise kannst du Aliasse nur für deinen eigenen Server hinzufügen. Du kannst einen primären Alias und beliebig viele Zweitaliasse hinzufügen.
+ Aliase dieses Raumes. Normalerweise kannst du Aliase nur für deinen eigenen Server hinzufügen. Du kannst einen primären Alias und beliebig viele Zweitaliase hinzufügen.
@@ -320,7 +320,7 @@
- Möchtest du wirklich eine privaten Chat mit %1 beginnen?
+ Möchtest du wirklich eine private Konversation mit %1 beginnen?
@@ -373,7 +373,7 @@ Wenn du glaubst, dass das ein Fehler ist, dann kannst du Nheko schließen und vi
- Du konntest %1 nicht betreten. Du kannst versuchen, anzuklopfen, so dass andere Leute dich einladen können. Möchtest du das tun?
+ Du konntest %1 nicht betreten. Du kannst versuchen anzuklopfen, so dass andere Leute dich einladen können. Möchtest du das tun?
Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen annehmen sollten:
@@ -399,17 +399,17 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Grund für den Rauswurf des Nutzers
+ Grund für das Entfernen des Nutzers
- Gib einen Grund für den Rauswurf von %1 (%2) ein oder drücke Eingabe für keinen Grund:
+ Grund warum %1 (%2) aus dem Raum geworfen wird oder die Entertaste drücken um keinen Grund anzugeben:
- Konnte %1 nicht aus %2 hinauswerfen: %3
+ Konnte %1 nicht aus %2 entfernen: %3
@@ -562,38 +562,38 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Anfragen, einen Raum zu betreten zu dürfen. Der Grund ist optional.
+ Anfragen einen Raum zu betreten. Ein Grund ist optional.
- Raum verlassen. Der Grund ist optional.
+ Raum verlassen. Ein Grund ist optional.
- Einen Nutzer in diesen Raum einladen. Der Grund ist optional.
+ Einen Nutzer in diesen Raum einladen. Ein Grund ist optional.
- Einen Nutzer aus diesem Raum werfen. Der Grund ist optional.
+ Einen Nutzer aus diesem Raum werfen. Ein Grund ist optional.
- Einen Nutzer aus diesen Raum verbannen. Der Grund ist optional.
+ Einen Nutzer von diesem Raum verbannen. Ein Grund ist optional.
- Verbannung eines Nutzers aufheben. Der Grund ist optional.
+ Verbannung eines Nutzers aufheben. Ein Grund ist optional.
- Ein Event oder alle lokal zwischengespeicherten Nachrichten eines Nutzers zurückziehen.
+ Eine bestimmte Nachricht oder alle lokal geladenen Nachrichten eines Nutzers löschen.
@@ -663,12 +663,12 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Sende eine Bot-Nachricht.
+ Sende eine Nachricht als wärst du ein Bot.
- Sende eine Bot-Nachricht, aber in Regenbogenfarben.
+ Sende eine Botnachricht, aber in Regenbogenfarben.
@@ -688,12 +688,12 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Verwandle diesen Raum zu einen Direktchat.
+ Verwandel diesen Raum in eine Direktnachricht.
- Verwandle diesen Direktchat zu einen Raum.
+ Verwandle diese Direktnachricht in einen normalen Chatraum.
@@ -734,12 +734,12 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Direktchats
+ Direktnachrichten
- Zeige Direktchats an.
+ Zeige 1:1 Konversationen an.
@@ -749,7 +749,7 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Von dir favorisierte Räume.
+ Favorisierte Räume.
@@ -815,12 +815,12 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Möchtest du den Raum betreten? Du kannst unten einen Grund angeben (optional):
+ Möchtest du den Raum betreten? Du kannst unten einen Grund angeben:
- Dieser Raum kann nicht direkt betreten werden. Du kannst jedoch anklopfen, und Raummitglieder können diese Beitrittsanfrage akzeptieren oder ablehnen. Optional kannst du auch einen Grund angeben, um eingelassen zu werden:
+ Dieser Raum kann nicht direkt betreten werden. Du kanns aber anklopfen und die Personen in dem Raum können dich dann reinlassen oder auch nicht. Optional kannst du auch einen Grund angeben, warum sie das tun sollten.
@@ -848,7 +848,7 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- @nutzer.server.tld
+ @nutzer:server.tld
@@ -876,7 +876,7 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Neuer Raum
+ Neuer Chatraum
@@ -916,7 +916,7 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Alle eingeladenen Personen erhalten das gleiche Powerlevel wie der Ersteller
+ Alle eingeladenen Personen erhalten die gleichen Berechtigungen wie der Ersteller
@@ -967,7 +967,7 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Bitte verifiziere die folgenden Ziffern. Du solltest die gleichen Zahlen auf beiden Seiten sehen. Wenn diese sich unterscheiden, bitte klicke auf „Sie stimmen nicht überein!“, um die Verifizierung abzubrechen!
+ Bitte verifiziere die folgenden Ziffern. Stelle sicher dass beide Seiten die gleichen Zahlen sehen. Wenn diese sich unterscheiden, bitte klicke auf 'Sie stimmen nicht überein!' um die Verifizierung abzubrechen!
@@ -1038,12 +1038,12 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Bitte verifiziere die folgenden Emojis. Stelle sicher, dass du auf beiden Seiten das gleiche Emoji siehst. Wenn diese sich unterscheiden, bitte klicke auf „Sie stimmen nicht überein!“, um die Verifizierung abzubrechen!
+ Bitte verifiziere die folgenden Emoji. Stelle sicher dass beide Seiten die gleichen Emoji sehen. Wenn diese sich unterscheiden, bitte klicke auf 'Sie stimmen nicht überein!' um die Verifizierung abzubrechen!
- Je nach Schriftart können die angezeigten Emojis sich in unterschiedlichen Applikationen leicht unterscheiden. Auf die selbe Art kann sich die Übersetzung unter dem Emoji je nach Sprache unterscheiden. Trotzdem sollten die 64 möglichen Zeichen eindeutig genug sein. Z.B. sind eine Katze und ein Löwe unterschiedlich, aber in der einen Applikation ist die Katze eventuell nur als Gesicht dargestellt und nicht als ganze Katze.
+ Je nach Schriftart können die angezeigten Emoji sich in unterschiedlichen Applikationen leicht unterscheiden. Auf die selbe Art kann sich die Übersetzung unter dem Emoji je nach Sprache unterscheiden. Trotzdem sollten die 64 möglichen Zeichen eindeutig genug sein. Z.B. sind eine Katze und ein Löwe unterschiedlich, aber in der einen Applikation ist die Katze eventuell nur als Gesicht dargestellt und nicht als ganze Katze.
@@ -1071,7 +1071,7 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Beim Lesen des Entschlüsselungsschlüssels aus der Datenbank ist ein interner Fehler aufgetreten.
+ Es ist ein interner Fehler beim Laden des Schlüssels aus der Datenbank aufgetreten.
@@ -1081,12 +1081,12 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Die Nachricht konnte nicht geparst werden.
+ Nheko hat die Nachricht nach der Entschlüsselung nicht verstanden.
- Der Verschlüsselungsschlüssel wurde wiederverwendet. Möglicherweise versucht jemand, falsche Nachrichten in diesen Chat einzufügen!
+ Der Schlüssel dieser Nachricht wurde schon einmal verwendet! Eventuell versucht jemand, falsche Nachrichten in diese Unterhaltung einzufügen!
@@ -1155,7 +1155,7 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Zeitlimit für die Geräteverifizierung abgelaufen.
+ Verifizierung abgelaufen, die andere Seite antwortet nicht.
@@ -1220,7 +1220,7 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Benutzerevents
+ Benutzeränderungen
@@ -1230,7 +1230,7 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Powerlevelveränderungen
+ Berechtigungsveränderungen
@@ -1248,7 +1248,7 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Bilderpaket bearbeiten
+ Bilderpackung bearbeiten
@@ -1263,17 +1263,17 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Wähle Bilder für dieses Paket
+ Wähle Bilder für diese Packung
- Zum Paket hinzufügen
+ Zur Packung hinzufügen
- Ändere das Vorschaubild dieses Pakets
+ Ändere das Vorschaubild dieser Packung
@@ -1283,12 +1283,12 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Wähle ein Vorschaubild für das Paket aus
+ Wähle ein Vorschaubild für diese Packung aus
- Zustandsschlüssel
+ Eindeutiger Name
@@ -1338,37 +1338,37 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Bilderpaketeinstellungen
+ Bilderpackungseinstellungen
- Kontopaket erstellen
+ Neue private Packung
- Neues Raumpaket
+ Neue raumspezifische Packung
- Privates Paket
+ Private Packung
- Paket für diesen Raum
+ Packung aus diesem Raum
- Paket von übergeordneter Gruppe
+ Packung von übergeordneter Gruppe
- Global aktiviertes Paket
+ Global aktivierte Packung
@@ -1378,7 +1378,7 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Macht dieses Paket in allen Räumen verfügbar
+ Macht diese Packung in allen Räumen verfügbar
@@ -1393,11 +1393,6 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
InputBar
-
-
-
- Datei auswählen
-
@@ -1406,7 +1401,7 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
- Das Hochladen von „%1“ ist fehlgeschlagen
+ Das Hochladen von '%1' ist fehlgeschlagen
@@ -1492,10 +1487,10 @@ Du kannst zusätzlich einen Grund angeben, warum die anderen dein Anklopfen anne
You can also put your homeserver address there if your server doesn't support .well-known lookup.
Example: @user:server.my
If Nheko fails to discover your homeserver, it will show you a field to enter the server manually.
- Dein Anmeldename. Eine mxid sollte mit einem „@“ anfangen gefolgt von der Nutzer-ID. Nach dem Nutzernamen folgt dein Servername, getrennt durch einen „:“.
-Wenn dein Server den „.well-known“-Lookup nicht unterstützt, kannst du auch deine Heimserveradresse angeben.
+ Dein Anmeldename. Eine mxid sollte mit eine @ anfangen gefolgt von der Nutzerid. Nach dem Nutzernamen folgt der servername, getrennt durch ein :.
+Wenn dein Server keinen .well-known unterstützt, kannst du auch eine Serveradresse angeben.
Beispiel: @nutzer:mein.server
-Wenn Nheko den Server nicht finden kann, wird es dir ein Eingabefeld anzeigen, in das du den Server manuell eingeben kannst.
+Wenn Nheko den Server nicht finden kann, wird es dich nach der Serveradresse fragen.
@@ -1532,7 +1527,7 @@ Beispiel: https://mein.server:8787
- mein.server:8787
+ dein.server:8787
@@ -1560,7 +1555,7 @@ Beispiel: https://mein.server:8787
- Automatische Erkennung fehlgeschlagen. Unbekannter Fehler bei Anfrage von „.well-known“.
+ Automatische Erkennung fehlgeschlagen. Unbekannter Fehler bei Anfrage .well-known.
@@ -1570,17 +1565,17 @@ Beispiel: https://mein.server:8787
- Erhaltene Antwort war fehlerhaft. Bitte prüfe, ob die Heimserver-Domain gültig ist.
+ Erhaltene Antwort war fehlerhaft. Bitte Homeserverdomain prüfen.
- Ein unbekannter Fehler ist aufgetreten. Bitte prüfe, ob die Heimserver-Domain gültig ist.
+ Ein unbekannter Fehler ist aufgetreten. Bitte Homeserverdomain prüfen.
- Der ausgewählte Server unterstützt keine Version des Matrixprotokolls, die Nheko versteht (v1.1 bis v1.5). Du kannst dich nicht anmelden.
+ Der ausgewählte Server unterstützt keine der Matrix versionen, die Nheko versteht (v1.1 bis v1.5). Du kannst dich nicht anmelden.
@@ -1610,7 +1605,7 @@ Beispiel: https://mein.server:8787
- SSO-ANMELDUNG
+ SSO ANMELDUNG
@@ -1620,7 +1615,7 @@ Beispiel: https://mein.server:8787
- SSO-Anmeldung fehlgeschlagen
+ SSO Anmeldung fehlgeschlagen
@@ -1670,7 +1665,7 @@ Beispiel: https://mein.server:8787
- %2 hat den Raumnamen geändert zu: %1
+ %2 hat den Raumnamen geändert auf: %1
@@ -1680,7 +1675,7 @@ Beispiel: https://mein.server:8787
- %2 hat das Thema geändert zu: %1
+ %2 hat das Thema geändert auf: %1
@@ -1690,7 +1685,7 @@ Beispiel: https://mein.server:8787
- %1 hat das Raumavatar geändert
+ %1 hat dem Raumavatar geändert
@@ -1784,7 +1779,7 @@ Beispiel: https://mein.server:8787
- Schreibe eine Nachricht …
+ Schreibe eine Nachricht…
@@ -1817,7 +1812,7 @@ Beispiel: https://mein.server:8787
- Reagieren
+ Reaktion senden
@@ -1847,7 +1842,7 @@ Beispiel: https://mein.server:8787
- Grund für die Löschung eingeben oder Eingabetaste drücken für keinen Grund:
+ Grund für das Nachrichtenlöschen oder Entertaste drücken für keinen Grund:
@@ -1977,12 +1972,12 @@ Beispiel: https://mein.server:8787
- %1 mit dem Gerät %2 hat angefragt, verifiziert zu werden.
+ %1 mit dem Gerät %2 hat angefragt verifiziert zu werden.
- Dein Gerät (%1) hat angefragt, verifiziert zu werden.
+ Dein Gerät %1 hat angefragt verifiziert zu werden.
@@ -2157,7 +2152,7 @@ Beispiel: https://mein.server:8787
- Verschiebe Nutzer hoch oder runter, um ihre Berechtigungen zu ändern
+ Verschiebe Nutzer zwischen Rollen um deren Rolle zu ändern
@@ -2195,7 +2190,7 @@ Beispiel: https://mein.server:8787
- Keine Berechtigung, die Berechtigungen hier zu verändern
+ Keine Berechtigung die Berechtigungen hier zu verändern
@@ -2218,7 +2213,7 @@ Beispiel: https://mein.server:8787
- Konnte Powerlevel nicht aktualisieren: %1
+ Konnte Berechtigungen nicht aktualisieren: %1
@@ -2226,7 +2221,7 @@ Beispiel: https://mein.server:8787
- Konnte Powerlevel nicht aktualisieren: %1
+ Konnte Berechtigungen nicht aktualisieren: %1
@@ -2259,7 +2254,7 @@ Beispiel: https://mein.server:8787
- Von anderen Teilnehmern gesendete Events zurückziehen
+ Fremde Nachrichten löschen
@@ -2269,7 +2264,7 @@ Beispiel: https://mein.server:8787
- Veraltete Aliasse-Events
+ Veraltetes Raumaddressenevent
@@ -2339,12 +2334,12 @@ Beispiel: https://mein.server:8787
- Eigene Events zurückziehen
+ Eigene Nachrichten löschen
- Angeheftete Events ändern
+ Angeheftete Nachrichten ändern
@@ -2478,7 +2473,7 @@ Beispiel: https://mein.server:8787
- Schreibe eine Nachricht …
+ Schreibe eine Nachricht…
@@ -2573,7 +2568,7 @@ Beispiel: https://mein.server:8787
- Automatische Erkennung fehlgeschlagen. Unbekannter Fehler bei Anfrage nach „.well-known“.
+ Automatische Erkennung fehlgeschlagen. Unbekannter Fehler bei Anfrage .well-known.
@@ -2583,12 +2578,12 @@ Beispiel: https://mein.server:8787
- Erhaltene Antwort war fehlerhaft. Bitte prüfe, ob die Heimserver-Domain gültig ist.
+ Erhaltene Antwort war fehlerhaft. Bitte Homeserverdomain prüfen.
- Ein unbekannter Fehler ist aufgetreten. Bitte prüfe, ob die Heimserver-Domain gültig ist.
+ Ein unbekannter Fehler ist aufgetreten. Bitte Homeserverdomain prüfen.
@@ -2670,12 +2665,12 @@ Beispiel: https://mein.server:8787
- Neues Tag
+ Neuer Tag
- Gib das Tag, das du verwenden willst, ein:
+ Gib den Tag, den du verwenden willst, ein:
@@ -2720,12 +2715,12 @@ Beispiel: https://mein.server:8787
- Neues Tag erstellen …
+ Neuen Tag erstellen…
- Zu Gruppe hinzufügen oder entfernen ...
+ Zu Gruppe hinzufügen oder entfernen...
@@ -2840,7 +2835,7 @@ Beispiel: https://mein.server:8787
- Suchen ...
+ Suchen...
@@ -2860,7 +2855,7 @@ Beispiel: https://mein.server:8787
- Powerlevel
+ Berechtigung
@@ -2993,7 +2988,7 @@ Bitte beachte, dass die Verschlüsselung hinterher nicht mehr deaktiviert werden
- Addressen/Aliasse dieses Raums betrachen und ändern
+ Raumadressen anzeigen und ändern
@@ -3046,7 +3041,7 @@ Bitte beachte, dass die Verschlüsselung hinterher nicht mehr deaktiviert werden
- Erlaube Gästen, beizutreten
+ Erlaube Gästen beizutreten
@@ -3056,12 +3051,12 @@ Bitte beachte, dass die Verschlüsselung hinterher nicht mehr deaktiviert werden
- Aliasse
+ Raumaddressen
- Ändere, welche Pakete aktiv sind, entferne oder erstelle neue Pakete
+ Ändere welche Packungen aktiv sind, entferne oder erstelle neue Packungen.
@@ -3153,22 +3148,22 @@ Bitte beachte, dass die Verschlüsselung hinterher nicht mehr deaktiviert werden
- Bitte gib dein Anmeldepasswort ein, um fortzufahren:
+ Bitte gib dein Anmeldepassword an um fortzufahren:
- Bitte gib eine gültige E-Mail-Adresse an, um fortzufahren:
+ Bitte gib eine gültige Emailadresse an um fortzufahren:
- Bitte gib eine gültige Telefonnummer an, um fortzufahren:
+ Bitte gib eine gültige Telefonnummer an um fortzufahren:
- Bitte gib das Token ein, das dir geschickt wurde:
+ Bitte gib das Token ein, dass dir geschickt wurde:
@@ -3240,7 +3235,7 @@ Bitte beachte, dass die Verschlüsselung hinterher nicht mehr deaktiviert werden
- Nheko konnte sich nicht mit dem Dienst zum sicheren Speichern von Verschlüsselungsgeheimnissen verbinden. Das kann verschiedene Gründe haben. Prüfe, ob der D-Bus-Dienst aktiv ist und du einen Dienst wie KWallet, Gnome Keyring, KeePassXC oder das Äquivalent für deine Plattform konfiguriert hast. Wenn du Probleme hast, kannst du gerne einen Issue hier eröffnen (auf Englisch): https://github.com/Nheko-Reborn/nheko/issues
+ Nheko konnte sich nicht mit dem Dienst zum sicheren speichern von Schlüsseln verbinden. Das kann verschiedene Gründe haben. Prüfe, ob der D-Bus-Dienst aktiv ist und du einen Dienst wie KWallet, Gnome Keyring, KeePassXC oder das Äquivalent für deine Platform. Wenn du Probleme hast, scheue dich nicht Hilfe hier zu suchen: https://github.com/Nheko-Reborn/nheko/issues
@@ -3248,7 +3243,7 @@ Bitte beachte, dass die Verschlüsselung hinterher nicht mehr deaktiviert werden
- Dies ist dein Wiederherstellungsschlüssel. Du brauchst ihn, um auf deine verschüsselten Nachrichten und Verifizierungsschlüssel zugreifen zu können. Pass gut auf ihn auf. Teile den Schlüssel mit niemandem und verliere ihn nicht! Gehe nicht über Los! Ziehe keine 2000€ ein!
+ Dies ist dein Wiederherstellungsschlüssel. Du brauchst diesen um auf deine verschüsselten Nachrichten und Verifizierungsschlüssel zugreifen zu können. Pass gut drauf auf. Teile den Schlüssel mit niemandem und verliere ihn nicht! Gehe nicht über Los! Ziehe nicht 2000€ ein!
@@ -3270,7 +3265,7 @@ Bitte beachte, dass die Verschlüsselung hinterher nicht mehr deaktiviert werden
Hallo und willkommen zu Matrix!
-Sieht so aus, als wärst du neu hier. Bevor du deine Nachrichten sicher verschlüsseln kannst, müssen wir ein paar Sachen konfigurieren. Du kannst entweder sofort auf „Akzeptieren“ drücken, oder ein paar grundlegende Einstellungen anpassen. Wir versuchen, einige der Grundlagen zu erklären. Du kannst diese Teile überspringen, aber sie könnten hilfreich sein!
+Sieht so aus als wärst du neu hier. Bevor wir deine Nachrichten verschlüsseln können, müssen wir ein paar Sachen konfigurieren. Keine Panik, du kannst auch einfach weiter klicken, ohne irgendentwas umzustellen, aber du kannst natürlich auch ein paar der Optionen ändern. Die Erklärungen sind etwas länger in der Hoffnung, dass sie weiterhelfen. Du kannst sie überspringen, aber einmal durchlesen ist vielleicht vorteilhaft!
@@ -3281,7 +3276,7 @@ Sieht so aus, als wärst du neu hier. Bevor du deine Nachrichten sicher verschl
- Es sieht so aus, als hättest du die Verschlüsselung für dieses Konto schon aktiviert. Damit dieses Gerät auf verschlüsselte Nachrichten zugreifen kann und vertrauenswürdig erscheint, kannst du es jetzt entweder mit einem anderen Gerät verifizieren oder (wenn du einen hast) mit deinem Wiederherstellungsschlüssel bestätigen. Bitte wähle eine der folgenden Optionen.
+ Es sieht so aus als hättest du die Verschlüsselung für dieses Konto schon aktiviert. Damit dieses Gerät auf verschlüsselte Nachrichten zugreifen kann und vertrauenswürdig erschein, kannst du es jetzt entweder mit einem anderen Gerät verifizieren oder (wenn du einen hast) mit deinem Wiederherstellungsschlüssel bestätigen. Bitte wähle eine der folgenden Optionen.
Wenn du verifizieren wählst, musst du dein anderes Gerät zur Hand haben. Wenn du den Wiederherstellungsschlüssel wählst, brauchst du deine Wiederherstellungsphrase oder -passwort. Mit Abbrechen kannst du diesen Schritt auf später verschieben.
@@ -3310,7 +3305,7 @@ Wenn du verifizieren wählst, musst du dein anderes Gerät zur Hand haben. Wenn
- Konnte Schlüssel für den sicheren serverseitigen Speicher nicht erzeugen!
+ Konnte Schlüssel für den sicheren, server-seitigen Speicher nicht erzeugen!
@@ -3325,7 +3320,7 @@ Wenn du verifizieren wählst, musst du dein anderes Gerät zur Hand haben. Wenn
- Der Identitätschlüssel hat sich geändert. Das macht die Ende-zu-Ende-Verschlüsselung kaputt, also wirst du abgemeldet.
+ Die Identitätschlüssel haben sich geändert. Das stört die Verschlüsselung, deswegen wirst du abgemeldet.
@@ -3334,12 +3329,12 @@ Wenn du verifizieren wählst, musst du dein anderes Gerät zur Hand haben. Wenn
- Konnte das Bilderpaket nicht aktualisieren: %1
+ Konnte die Bilderpackung nicht aktualisieren: %1
- Konnte das alte Bilderpaket nicht löschen: %1
+ Konnte die alte Bilderpackung nicht löschen: %1
@@ -3447,13 +3442,13 @@ Wenn du verifizieren wählst, musst du dein anderes Gerät zur Hand haben. Wenn
- Zurückziehen der Nachricht fehlgeschlagen: %1
+ Nachricht zurückziehen fehlgeschlagen: %1
- Event konnte nicht verschlüsselt werden, das Versenden wurde abgebrochen!
+ Event konnte nicht verschlüsselt werden, senden wurde abgebrochen!
@@ -3492,32 +3487,32 @@ Wenn du verifizieren wählst, musst du dein anderes Gerät zur Hand haben. Wenn
- %1 hat erlaubt, diesen Raum durch Anklopfen beizutreten.
+ %1 hat erlaubt Leuten diesen Raum durch Anklopfen beizutreten.
- %1 hat Mitgliedern aus den folgenden Räumen erlaubt, diesem Raum automatisch beizutreten: %2
+ %1 hat Mitgliedern aus folgenden Räumen erlaubt diesen Raum automatisch zu betreten: %2
- %1 hat Gästen erlaubt, den Raum zu betreten.
+ %1 hat Gästen erlaubt den Raum zu betreten.
- %1 hat Gästen verboten, den Raum zu betreten.
+ %1 hat Gästen verboten den Raum zu betreten.
- %1 hat den Raum lesbar für alle gemacht. Nutzer, die nicht Teilnehmer dieses Raums sind, können nun Events in diesem Raum lesen.
+ %1 hat den Raum lesbar für alle gemacht. Nutzer, die nicht Teilnehmer dieses Raums sind, können nun Nachrichten in diesem Raum lesen.
- %1 hat die Raumhistorie von diesen Zeitpunkt an sichtbar für Mitglieder gemacht.
+ %1 hat eingestellt, dass nur Teilnehmer Nachrichten in diesem Raum lesen können (ab diesem Punkt).
@@ -3538,61 +3533,61 @@ Wenn du verifizieren wählst, musst du dein anderes Gerät zur Hand haben. Wenn
- %1 hat das nötige Powerlevel, um Leute aus dem Raum zu werfen, von %2 auf %3 geändert.
+ %1 hat die benötigte Berechtigungen um Leute aus dem Raum zu werfen von %2 auf %3 geändert.
- %n Mitglied kann nun Raummitglieder hinauswerfen.
- %n Mitglieder können nun Raummitglieder hinauswerfen.
+ %n Teilnehmer kann nun Mitglieder aus dem Raum entfernen.
+ %n Teilnehmer können nun Mitglieder aus dem Raum entfernen.
- %1 kann nun Raummitglieder aus den Raum werfen.
+ %1 kann nun andere Nutzer aus dem Raum werfen.
- %1 hat das nötige Powerlevel, um Nachrichten in diesem Raum zurückzuziehen, von %2 auf %3 geändert.
+ %1 hat die nötigen Berechtigungen um Nachrichten in diesem Raum zu löschen von %2 auf %3 geändert.
- %n Mitglied kann nun Raumnachrichten zurückziehen.
- %n Mitglieder können nun Raumnachrichten zurückziehen.
+ %1 Teilnehmer kann nun Nachrichten löschen.
+ %1 Teilnehmer können nun Nachrichten löschen.
- %1 kann nun Nachrichten zurückziehen.
+ %1 kann nun Nachrichten löschen.
- %1 hat das nötige Powerlevel, um Leute aus dem Raum zu verbannen, von %2 auf %3 geändert.
+ %1 hat die benötigten Berechtigungen um Leute aus dem Raum zu verbannen von %2 auf %3 geändert.
- %n Mitglied kann nun Raummitglieder verbannen.
- %n Mitglieder können nun Raummitglieder verbannen.
+ %n Teilnehmer kann nun Leute aus dem Rau verbannen.
+ %n Teilnehmer können nun Leute aus dem Rau verbannen.
- %1 kann nun Raummitglieder verbannen.
+ %1 kann nun Leute aus dem Raum verbannen.
- %1 hat das Powerlevel für die restlichen Zustandsevents (state_default) von %2 auf %3 geändert.
+ %1 hat das Berechtigungslevel für die restlichen Zustandsevents von %2 auf %3 geändert.
@@ -3610,67 +3605,67 @@ Wenn du verifizieren wählst, musst du dein anderes Gerät zur Hand haben. Wenn
- %1 hat das nötige Powerlevel, um Nutzer einzuladen, von %2 auf %3 geändert.
+ %1 hat die nötigen Berechtigungen um Nutzer einzuladen von %2 auf %3 geändert.
- %1 hat das „events_default“-Powerlevel von %2 auf %3 geändert. Neue Nutzer können ab jetzt keine Events mehr senden.
+ %1 hat die nötigen Berechtigungen für beliebige Events von %2 auf %3 geändert. Neue Nutzer können jetzt keine Events mehr senden.
- %1 hat das nötige „events_default“-Powerlevel von %2 auf %3 geändert. Neue Nutzer können jetzt Events senden, solange keine anderen Einschränkungen vorliegen.
+ %1 hat die nötigen Berechtigungen für beliebige Events von %2 auf %3 geändert. Neue Nutzer können jetzt Events senden, solange keine anderen Einschränkungen vorliegen.
- %1 hat das „events_default“-Powerlevel dieses Raumes von %2 auf %3 geändert.
+ %1 hat die nötigen Berechtigungen für beliebige Events von %2 auf %3 geändert.
- %1 hat %2 zum Administrator dieses Raumes befördert.
+ %1 hat %2 zum Administrator befördert.
- %1 hat %2 zu einem Moderator in dieses Raumes gemacht.
+ %1 hat %2 zu einem Moderator gemacht.
- %1 hat %2 zum Moderator dieses Raumes degradiert.
+ %1 hat %2 zum Moderator degradiert.
- %1 hat das Powerlevel von %2 von %3 auf %4 geändert.
+ %1 hat die Berechtigungen von %2 von %3 auf %4 geändert.
- %1 hat nur Administratoren erlaubt, „%2“ zu senden.
+ %1 hat nur Administratoren erlaubt "%2" zu senden.
- %1 hat nur Moderatoren erlaubt, „%2“ zu senden.
+ %1 hat nur Moderatoren erlaubt "%2" zu senden.
- %1 hat allen erlaubt, „%2“ zu senden.
+ %1 hat allen erlaubt "%2" zu senden.
- %1 hat das Powerlevel für den Eventtyp „%2“ vom Standardwert auf %3 geändert.
+ %1 hat die Berechtigungen für Events vom Typ "%2" vom Standard auf %3 geändert.
- %1 hat das nötige Powerlevel für Events vom Typ „%2“ von %3 auf %4 geändert.
+ %1 hat die nötigen Berechtigungen für Events vom Typ "%2" von %3 auf %4 geändert.
@@ -3680,12 +3675,12 @@ Wenn du verifizieren wählst, musst du dein anderes Gerät zur Hand haben. Wenn
- %1 hat die folgenden Bilder vom Paket %2 entfernt:<br>%3
+ %1 hat die folgenden Bilder vom der Bilderpackung %2 entfernt:<br>%3
- %1 hat die folgenden Bilder zum Paket %2 hinzugefügt:<br>%3
+ %1 hat die folgenden Bilder zu der Bilderpackung %2 hinzugefügt:<br>%3
@@ -3695,32 +3690,32 @@ Wenn du verifizieren wählst, musst du dein anderes Gerät zur Hand haben. Wenn
- %1 hat die Regel zum Verbannen von Nutzern, deren Name mit %2 zusammenpasst, deaktiviert.
+ %1 hat die Regel zum bannen von Nutzern die %2 entsprechen deaktiviert.
- %1 hat eine Regel für das Verbannen von Nutzern, deren Name mit %2 zusammenpasst, hinzugefügt. Grund: „%3“.
+ %1 hat eine Regel für das Verbannen von Nutzern wegen '%3', die %2 entsprechen, hinzugefügt.
- %1 hat die Regel zum Verbannen von Räumen, deren Name mit %2 zusammenpasst, deaktiviert.
+ %1 hat die Regel zum bannen von Räumen, die %2 entsprechen deaktiviert.
- %1 hat eine Regel für das Verbannen von Räumen, deren Namem mit %2 zusammenpasst, hinzugefügt. Grund: „%3“.
+ %1 hat eine Regel für das Verbannen von Räumen wegen '%3', die %2 entsprechen, hinzugefügt.
- %1 hat die Regel zum Verbannen von Servern, deren Name mit %2 zusammenpasst, deaktiviert.
+ %1 hat die Regel zum bannen von Servern, die %2 entsprechen deaktiviert.
- %1 hat die Regel zum Verbannen von Servern, deren Name mit %2 zusammenpasst, hinzugefügt. Grund: „%3“.
+ %1 hat eine Regel für das Verbannen von Servern wegen '%3', die %2 entsprechen, hinzugefügt.
@@ -3782,7 +3777,7 @@ Grund: %4
- %1 hat den Raum durch Autorisierung vom Server von %2 betreten.
+ %1 hat den Raum durch Authorisierung von %2s Server betreten.
@@ -3802,7 +3797,7 @@ Grund: %4
- %1 hat das eigene Anklopfen zurückgezogen.
+ %1 hat das Anklopfen zurückgezogen.
@@ -3833,7 +3828,7 @@ Grund: %4
This is a leave event after the user already left and shouldn't happen apart from state resets
- %1 hat den Raum verlassen, nachdem der Raum bereits von dieser Person verlassen wurde!
+ %1 hat den Raum verlassen, obwohl er gar nicht mehr am Raum teilnahm!
@@ -4205,7 +4200,7 @@ Grund: %4
- Großes Emoji in der Nachrichtenliste
+ Große Emoji in der Nachrichtenliste
@@ -4225,7 +4220,7 @@ Grund: %4
- Nachrichten als Markdown formatiert senden
+ Sende Nachrichten als Markdown formatiert
@@ -4315,17 +4310,17 @@ Grund: %4
- Ausgefallene Chateffekte wie Konfetti anzeigen
+ Lustige Chateffekte wie Konfetti anzeigen
- Animationen reduzieren oder deaktivieren
+ Reduziere oder deaktiviere Animationen
- Sichtschutz
+ Blickschutz
@@ -4380,7 +4375,7 @@ Grund: %4
- Erlaube, den Fallback-Assistenzserver zu verwenden
+ Erlaube den Fallbackassistenzserver zu verwenden
@@ -4405,7 +4400,7 @@ Grund: %4
- Benutzer-ID
+ Anmeldename
@@ -4495,7 +4490,7 @@ Grund: %4
- Online-Backupschlüssel
+ Online Backupschlüssel
@@ -4525,16 +4520,16 @@ Grund: %4
- Setze den zu spielenden Benachrichtigungston für eingehende Anrufseinladungen
+ Ändere den Klingelton für eingehende Anrufe
- Zeitbegrenzung (in Sekunden), bis der Bildschirm verschwommen angezeigt wird, nachdem das Fenster den Fokus verliert.
-Bei 0 wird der Sichtschutz sofort aktiv.
-Maximaler Wert ist eine Stunde (3600 Sekunden)
+ Zeitbegrenzung (in Sekunden) bis der Bildschirm verschwommen angezeigt wird, nachdem das Fenster den Fokus verliert.
+Bei 0 wird der Blickschutz sofort aktiv.
+Maximaler Wert ist eine Stunde (3600 Sekunden).
@@ -4544,7 +4539,7 @@ Maximaler Wert ist eine Stunde (3600 Sekunden)
- Erhöht die Schriftgröße, wenn die Nachricht nur aus ein paar Emojis besteht.
+ Erhöht die Schriftgröße, wenn die Nachricht nur aus ein paar Emoji besteht.
@@ -4554,7 +4549,7 @@ Maximaler Wert ist eine Stunde (3600 Sekunden)
- Starte die Applikation im Hintergrund, ohne ein Fenster zu öffnen.
+ Starte die Applikation im Hintergrund ohne ein Fenster zu öffnen.
@@ -4565,13 +4560,13 @@ Maximaler Wert ist eine Stunde (3600 Sekunden)
- Die Verwendung von Markdown in Nachrichten erlauben.
-Wenn deaktiviert, werden alle Nachrichten als unformatierter Text gesendet.
+ Nutze Markdown als Format für Nachrichten.
+Wenn deaktiviert werden alle Nachrichten als unformatierter Text gesendet.
- Kehrt die Bedeutung der Eingabetaste im Texteingabefeld um, wodurch die Nachricht mit Umschalt+Eingabe gesendet wird und mit der Eingabetaste allein wird eine neue Zeile begonnen.
+ Erlaubt das invertieren des Verhaltens der Enter taste. Kontrolliert ob Umschalt+Enter eine Neuzeile einfügt oder die Nachricht sendet.
@@ -4581,7 +4576,7 @@ Wenn deaktiviert, werden alle Nachrichten als unformatierter Text gesendet.
- Die Größe von Avataren wird angepasst, damit sie oberhalb der Nachricht passen.
+ Benutzerbilder werden verkleinert um über die Nachricht zu passen.
@@ -4592,55 +4587,55 @@ Wenn deaktiviert, werden alle Nachrichten als unformatierter Text gesendet.
- Anzeigen, wer gerade in einem Raum tippt.
-Diese Einstellung legt auch fest, ob das eigene Tippen an andere gesendet wird.
+ Zeige wer gerade in einem Raum tippt.
+Diese Einstellung steuert auch, ob das eigene Tippen an andere gesendet wird.
- Zeige Buttons für das schnelle Antworten, Reagieren und zusätzliche Optionen neben jeder Nachricht.
+ Zeige Knöpfe für das schnelle Antworten, Reagieren und zusätzliche Optionen neben jeder Nachricht.
- Benachrichtigungen über neue Nachrichten, wenn der Client nicht im Vordergrund ist.
+ Benachrichtigungen pber neue Nachrichten, wenn der Client nicht im Vordergrund ist.
- Ändere das Aussehen der Chat-Avatare.
-AUS: Quadratisch; AN: Kreisförmig.
+ Ändere das aussehen der Chatavatare.
+AUS - Quadratisch, AN - Kreisförmig.
- Nachrichten, die in den Benachrichtigungen für verschlüsselte Chats angezeigt werden, entschlüsseln.
+ Nachrichten in Benachrichtigungen entschlüsseln in verschlüsselten Räumen.
- Wähl aus, wo die Gesamtzahl der Benachrichtigungen innerhalb einer Gemeinschaft oder eines Tags gezeigt werden soll.
+ Wähle aus ob die Benachrichtigungsanzahl für Gruppen und Tags angezeigt werden oder nicht.
- Manche Nachrichten können mit ausgefallenen Effekten versendet werden. Zum Beispiel werden Nachrichten, die mit „/confetti“ gesendet wurden, Konfetti auf den Bildschirm zeigen.
+ Manche Nachrichten lösen extra Effekte aus. Z.B. erzeugen Nachrichten, die mit /confetti gesendet wurden, einen kleinen Konfettischauer.
- Für besseres Aussehen verwendet Nheko an verschiedenen Stellen Animationen. Diese Option erlaubt dir, die Animationen zu deaktiveren, wenn diese bei dir Unwohlsein hervorrufen.
+ Für besseres Aussehen verwendet Nheko an verschiedenen Stellen Animationen. Diese Option erlaubt dir die Animationen zu deaktiveren, wenn diese bei dir Unwohlsein hervorrufen.
- Antwortet automatisch auf Schlüsselanfragen von anderen Benutzern, wenn diese verifiziert sind, selbst, wenn das entsprechende Gerät sonst keinen Zugriff auf diese Schlüssel hätte.
+ Teilt automatisch Schlüssel für Nachrichten mit verifizierten Nutzern (auf Anfrage), selbst wenn diese sonst keinen Zugriff darauf hätten.
- Der Schlüssel, um deine eigenen Geräte zu verifizieren. Wenn dieser im Cache ist, dann wird die Verifizierung eines deiner Geräte es als verifiziert für all deine anderen Geräte markieren und für Benutzer, die dich verfifiziert haben.
+ Der Schlüssel um deine eigenen Geräte zu verifizieren. Wenn dieser im Cache ist, dann werden alle deine Geräte als verifiziert für andere Nutzer erscheinen, wenn du diese verifiziert hast.
@@ -4662,7 +4657,7 @@ Normalerweise animiert das den Taskbaricon oder färbt das Fenster orange ein.
- Setze eine maximale Breite für Nachrichten im Chat (in Pixeln). Das kann die Lesbarkeit auf breiten Bildschirmen, wenn Nheko maximiert ist, erhöhen
+ Setze eine maximale Breite für Nachrichten im Chat (in Pixeln). Das kann die Lesbarkeit auf breiten Bildschirmen erhöhen.
@@ -4723,7 +4718,7 @@ den Fokus verliert.
- Wird die Textauswahl in der Nachrichtenliste verhindern, um das Scrolling mit Touchscreens zu vereinfachen.
+ Deaktiviert Textselektion in Nachrichten, damit das nicht beim Scrollen mit den Fingern stört.
@@ -4748,12 +4743,12 @@ den Fokus verliert.
- Der Schlüssel, um Online-Schlüssel-Backups zu entschlüsseln. Wenn er im Cache liegt, kannst du das Online-Schlüssel-Backup aktivieren, um Verschlüsselungsschlüssel sicher verschlüsselt auf dem Server abzuspeichern.
+ Der Schlüssel um Schlüssel aus der Onlinesicherung zu laden. Wenn dieser vorhanden ist, können die Schlüssel für verschlüsselte Nachrichten sicher online gespeichert und wieder runtergeladen werden.
- Der Schlüssel, um andere Nutzer zu verifizieren. Wenn dieser lokal zwischengespeichert ist, dann wird die Verifikation eines Benutzers all seine Geräte verifizieren.
+ Der Schlüssel um andere Nutzer zu verifizieren. Wenn der lokal zwischengespeichert ist, dann werden durch eine Nutzerverifizierung alle Geräte verifiziert.
@@ -4765,9 +4760,9 @@ den Fokus verliert.
- Erlaubt anderen Anwendungen und Plugins, Informationen über die Räume, in denen du dich befindest, mittels der D-Bus-Schnittstelle zu laden. Dies kann nützlich sein, aber auch missbraucht werden. Aktivieren auf eigene Gefahr.
+ Erlaubt anderen Anwendungen informationen über deine Matrixräume durch die D-Bus Schnittstelle zu laden. Dies kann nützlich sein, aber auch missbraucht werden. Aktivieren auf eigene Gefahr.
-Diese Einstellung wird nach einem Neustart aktiv.
+Diese Einstellung benötigt einen Neustart von Nheko.
@@ -4798,7 +4793,7 @@ Diese Einstellung wird nach einem Neustart aktiv.
- Dateipasswort
+ Password für Datei
@@ -4865,7 +4860,7 @@ Diese Einstellung wird nach einem Neustart aktiv.
- Keinen verschlüsselten Privatchat mit diesem User gefunden. Erstelle einen verschlüsselten Privatchat mit diesem Nutzer und versuche es erneut.
+ Keinen verschlüsselten Chat mit diesem User gefunden. Erstelle einen verschlüsselten 1:1 Chat mit diesem Nutzer und versuche es erneut.
@@ -4873,7 +4868,7 @@ Diese Einstellung wird nach einem Neustart aktiv.
- Auf Gegenseite warten …
+ Auf Gegenseite warten…
@@ -4926,7 +4921,7 @@ Diese Einstellung wird nach einem Neustart aktiv.
- Für besseres Aussehen verwendet Nheko an verschiedenen Stellen Animationen. Diese Option erlaubt dir, die Animationen zu deaktiveren, wenn diese bei dir Unwohlsein hervorrufen.
+ Für besseres Aussehen verwendet Nheko an verschiedenen Stellen Animationen. Diese Option erlaubt dir die Animationen zu deaktiveren, wenn diese bei dir Unwohlsein hervorrufen.
@@ -4980,7 +4975,7 @@ Diese Einstellung wird nach einem Neustart aktiv.
- Löse das reCAPTCHA und drücke den Bestätigungsknopf
+ Löse das reCAPTCHA und drücke den "Bestätigen"-Knopf
diff --git a/resources/langs/nheko_el.ts b/resources/langs/nheko_el.ts
index 07c89a3e..b3e33751 100644
--- a/resources/langs/nheko_el.ts
+++ b/resources/langs/nheko_el.ts
@@ -1390,11 +1390,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
- Διάλεξε ένα αρχείο
-
diff --git a/resources/langs/nheko_eo.ts b/resources/langs/nheko_eo.ts
index 704da4a2..c87e169a 100644
--- a/resources/langs/nheko_eo.ts
+++ b/resources/langs/nheko_eo.ts
@@ -1394,11 +1394,6 @@ Vi povas aldoni noton, pri kial oni akceptu vian frapadon:
InputBar
-
-
-
- Elektu dosieron
-
diff --git a/resources/langs/nheko_es.ts b/resources/langs/nheko_es.ts
index 1f88c958..d5585db7 100644
--- a/resources/langs/nheko_es.ts
+++ b/resources/langs/nheko_es.ts
@@ -1392,11 +1392,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
- Seleccionar un archivo
-
diff --git a/resources/langs/nheko_et.ts b/resources/langs/nheko_et.ts
index 2221d7ed..380c3161 100644
--- a/resources/langs/nheko_et.ts
+++ b/resources/langs/nheko_et.ts
@@ -1393,11 +1393,6 @@ Kui soovid, siis võid lisada ka selgituse, miks peaks sinu koputusele reageerim
InputBar
-
-
-
- Vali fail
-
@@ -1408,6 +1403,11 @@ Kui soovid, siis võid lisada ka selgituse, miks peaks sinu koputusele reageerim
„%1“ üleslaadimine ei õnnestunud
+
+
+
+ Vali fail(id)
+
InviteDialog
diff --git a/resources/langs/nheko_fi.ts b/resources/langs/nheko_fi.ts
index 1213d13c..08c88899 100644
--- a/resources/langs/nheko_fi.ts
+++ b/resources/langs/nheko_fi.ts
@@ -1393,11 +1393,6 @@ Voit antaa valinnaisen syyn muiden hyväksyäkseen koputuksesi:
InputBar
-
-
-
- Valitse tiedosto
-
diff --git a/resources/langs/nheko_fr.ts b/resources/langs/nheko_fr.ts
index 2d71d13b..69820a0a 100644
--- a/resources/langs/nheko_fr.ts
+++ b/resources/langs/nheko_fr.ts
@@ -96,7 +96,7 @@
- Ajouter
+ Ajouter
@@ -229,7 +229,7 @@
-
+ Confirmer la déconnexion
@@ -239,7 +239,7 @@
- Impossible d'ouvrir la base de données, déconnexion !
+ Impossible d'ouvrir la base de données, déconnexion !
@@ -249,7 +249,7 @@
- Voulez-vous vraiment toquer à %1 ? Vous pouvez fournir une raison aux autres de l'accepter :
+ Voulez-vous vraiment frapper à %1 ? Vous pouvez donner une raison aux membres actuels de vous accepter :
@@ -275,7 +275,7 @@
- Voulez-vous vraiment inviter %1 (%2) ?
+ Voulez-vous vraiment inviter %1 (%2) ?
@@ -305,7 +305,7 @@
- Voulez-vous vraiment annuler le bannissement de %1 (%2) ?
+ Voulez-vous vraiment annuler le bannissement de %1 (%2) ?
@@ -325,19 +325,21 @@
- Échec de la migration du cache !
+ Échec de la migration du cache !
-
+ 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.
-
+ 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.
@@ -371,7 +373,8 @@ If you think this is a mistake, you can close Nheko instead to possibly recover
-
+ 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 :
@@ -381,7 +384,7 @@ You may optionally provide a reason for others to accept your knock:
- Impossible de supprimer l'invitation : %1
+ Impossible de supprimer l'invitation : %1
@@ -406,7 +409,7 @@ You may optionally provide a reason for others to accept your knock:
- Échec de l'expulsion de %1 de %2 : %3
+ Échec de l'expulsion de %1 de %2 : %3
@@ -429,27 +432,27 @@ You may optionally provide a reason for others to accept your knock:
-
+ /me <message>
-
+ /react <texte>
-
+ /part [raison]
-
+ /leave [raison]
-
+ /roomnick <nomaffiché>
@@ -565,57 +568,57 @@ You may optionally provide a reason for others to accept your knock:
-
+ Quitte un salon. La raison est optionnelle.
-
+ Invite un utilisateur dans le salon actuel. La raison est optionnelle.
-
+ Expulse un utilisateur du salon actuel. La raison est optionnelle.
-
+ Bannit un utilisateur du salon actuel. La raison est optionnelle.
-
+ Dé-bannit un utilisateur du salon actuel. La raison est optionnelle.
-
+ Efface un évènement ou tous les messages connus (présents dans la base de données locale) d'un utilisateur.
-
+ Change votre nom affiché dans ce salon.
-
+ ¯\_(ツ)_/¯ avec un message optionnel.
-
+ (╯°□°)╯︵ ┻━┻
-
+ ┯━┯╭( º _ º╭)
-
+ ノ┬─┬ノ ︵ ( \o°o)\
@@ -650,32 +653,32 @@ You may optionally provide a reason for others to accept your knock:
-
+ Envoie un message aux couleurs de l'arc-en-ciel.
-
+ Envoie /me aux couleurs de l'arc-en-ciel.
-
+ Envoie un message de robot.
-
+ Envoie un message de robot aux couleurs de l'arc-en-ciel.
-
+ Envoie un message avec des confettis.
-
+ Envoie des confettis avec un message aux couleurs de l'arc-en-ciel.
@@ -698,12 +701,12 @@ You may optionally provide a reason for others to accept your knock:
-
+ Ne pas afficher le compteur de notifications pour cette communauté ou cette étiquette.
-
+ Cache par défaut les salons avec cette étiquette ou provenant de cette communauté.
@@ -771,7 +774,7 @@ You may optionally provide a reason for others to accept your knock:
-
+ Erreur lors de la mise à jour de cette communauté : %1
@@ -804,7 +807,7 @@ You may optionally provide a reason for others to accept your knock:
-
+
%n membre
%n membres
@@ -964,17 +967,17 @@ You may optionally provide a reason for others to accept your knock:
- 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 !
+ 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 !
- Ils sont différents !
+ Ils sont différents !
- Ils sont identiques !
+ Ils sont identiques !
@@ -1035,7 +1038,7 @@ You may optionally provide a reason for others to accept your knock:
- 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 !
+ 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 !
@@ -1045,12 +1048,12 @@ You may optionally provide a reason for others to accept your knock:
- Ils sont différents !
+ Ils sont différents !
- Ils sont identiques !
+ Ils sont identiques !
@@ -1114,7 +1117,7 @@ You may optionally provide a reason for others to accept your knock:
- Ce message n'est pas chiffré !
+ Ce message n'est pas chiffré !
@@ -1147,7 +1150,7 @@ You may optionally provide a reason for others to accept your knock:
- Clés non correspondantes détectées !
+ Clés non correspondantes détectées !
@@ -1162,7 +1165,7 @@ You may optionally provide a reason for others to accept your knock:
- Messages de vérification reçus dans le désordre !
+ Messages de vérification reçus dans le désordre !
@@ -1390,11 +1393,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
- Sélectionnez un fichier
-
@@ -1543,7 +1541,7 @@ Example: https://server.my:8787
- Vous avez entré un identifiant Matrix invalide exemple correct : @moi:monserveur.example.com)
+ Vous avez entré un identifiant Matrix invalide exemple correct : @moi:monserveur.example.com)
@@ -1626,12 +1624,12 @@ Example: https://server.my:8787
- Un appel est en cours. Se déconnecter ?
+ Un appel est en cours. Se déconnecter ?
- Êtes-vous certain de vouloir vous déconnecter ?
+ Êtes-vous certain de vouloir vous déconnecter ?
@@ -2041,7 +2039,7 @@ Example: https://server.my:8787
- Appeler %1 ?
+ Appeler %1 ?
@@ -3174,17 +3172,17 @@ Veuillez noter qu'il ne pourra plus être désactivé par la suite.
- Partager le bureau avec %1 ?
+ Partager le bureau avec %1 ?
- Fenêtre :
+ Fenêtre :
- Fréquence d'images :
+ Fréquence d'images :
@@ -3233,7 +3231,7 @@ Veuillez noter qu'il ne pourra plus être désactivé par la suite.
- 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
+ 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
@@ -3241,7 +3239,7 @@ Veuillez noter qu'il ne pourra plus être désactivé par la suite.
- 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 !
+ 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 !
@@ -3262,8 +3260,8 @@ Veuillez noter qu'il ne pourra plus être désactivé par la suite.
- 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 !
+ 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 !
@@ -3293,17 +3291,17 @@ Si vous choisissez de vérifier, vous aurez besoin de l'autre appareil. Si
- Échec de la création des clés pour l'auto-vérification (cross-signing) !
+ Échec de la création des clés pour l'auto-vérification (cross-signing) !
- Échec de la création de clés pour la sauvegarde en ligne !
+ Échec de la création de clés pour la sauvegarde en ligne !
- Échec de la création des clés pour le stockage sécurisé côté serveur !
+ Échec de la création des clés pour le stockage sécurisé côté serveur !
@@ -3313,7 +3311,7 @@ Si vous choisissez de vérifier, vous aurez besoin de l'autre appareil. Si
- Échec de la configuration du chiffrement : %1
+ Échec de la configuration du chiffrement : %1
@@ -3426,7 +3424,7 @@ Si vous choisissez de vérifier, vous aurez besoin de l'autre appareil. Si
- Vérification réussie ! Les deux côtés ont vérifié leur appareil !
+ Vérification réussie ! Les deux côtés ont vérifié leur appareil !
@@ -3446,7 +3444,7 @@ Si vous choisissez de vérifier, vous aurez besoin de l'autre appareil. Si
- Échec du chiffrement de l'évènement, envoi abandonné !
+ Échec du chiffrement de l'évènement, envoi abandonné !
@@ -3826,7 +3824,7 @@ Raison : %4
This is a leave event after the user already left and shouldn't happen apart from state resets
- %1 a quitté le salon après l'avoir déjà quitté !
+ %1 a quitté le salon après l'avoir déjà quitté !
@@ -4863,17 +4861,17 @@ This setting will take effect upon restart.
- Attente que le correspondant accepte la demande de vérification.
+ Attente d'acceptation de la demande de vérification par le correspondant.
- Attente que le correspondant poursuive le processus de vérification.
+ Attente de la poursuite du processus de vérification par le correspondant.
- Attente que le correspondant termine le processus de vérification.
+ Attente de la fin du processus de vérification par le correspondant.
diff --git a/resources/langs/nheko_hu.ts b/resources/langs/nheko_hu.ts
index d1a3f5e7..803dc2fe 100644
--- a/resources/langs/nheko_hu.ts
+++ b/resources/langs/nheko_hu.ts
@@ -1388,11 +1388,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
- Fájl kiválasztása
-
diff --git a/resources/langs/nheko_id.ts b/resources/langs/nheko_id.ts
index 2b6dcd2c..7af25e13 100644
--- a/resources/langs/nheko_id.ts
+++ b/resources/langs/nheko_id.ts
@@ -1391,11 +1391,6 @@ Kamu dapat memberikan alasan untuk orang lain untuk menerima ketukanmu:
InputBar
-
-
-
- Pilih sebuah file
-
@@ -1406,6 +1401,11 @@ Kamu dapat memberikan alasan untuk orang lain untuk menerima ketukanmu:Upload of '%1' failed
Pengunggahan '%1' gagal
+
+
+
+ Pilih berkas
+
InviteDialog
diff --git a/resources/langs/nheko_ie.ts b/resources/langs/nheko_ie.ts
index c22fbb0e..4358c218 100644
--- a/resources/langs/nheko_ie.ts
+++ b/resources/langs/nheko_ie.ts
@@ -1390,11 +1390,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
-
-
diff --git a/resources/langs/nheko_it.ts b/resources/langs/nheko_it.ts
index d2144e3b..c6710f2a 100644
--- a/resources/langs/nheko_it.ts
+++ b/resources/langs/nheko_it.ts
@@ -1390,11 +1390,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
- Seleziona un file
-
diff --git a/resources/langs/nheko_ja.ts b/resources/langs/nheko_ja.ts
index 60f80848..55cf649f 100644
--- a/resources/langs/nheko_ja.ts
+++ b/resources/langs/nheko_ja.ts
@@ -1388,11 +1388,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
- ファイルを選択
-
diff --git a/resources/langs/nheko_ml.ts b/resources/langs/nheko_ml.ts
index ece960ad..0a912b3a 100644
--- a/resources/langs/nheko_ml.ts
+++ b/resources/langs/nheko_ml.ts
@@ -1390,11 +1390,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
- ഒരു ഫയൽ തിരഞ്ഞെടുക്കുക
-
diff --git a/resources/langs/nheko_nl.ts b/resources/langs/nheko_nl.ts
index 8b78215a..49dadbb3 100644
--- a/resources/langs/nheko_nl.ts
+++ b/resources/langs/nheko_nl.ts
@@ -1393,11 +1393,6 @@ Je kan optioneel hier een reden invoeren dat je aanklopt:
InputBar
-
-
-
- Selecteer een bestand
-
diff --git a/resources/langs/nheko_pl.ts b/resources/langs/nheko_pl.ts
index cfd22a8f..435cfe24 100644
--- a/resources/langs/nheko_pl.ts
+++ b/resources/langs/nheko_pl.ts
@@ -1392,11 +1392,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
- Wybierz plik
-
diff --git a/resources/langs/nheko_pt_BR.ts b/resources/langs/nheko_pt_BR.ts
index 53a9b9f1..43ae3f0b 100644
--- a/resources/langs/nheko_pt_BR.ts
+++ b/resources/langs/nheko_pt_BR.ts
@@ -1390,11 +1390,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
-
-
diff --git a/resources/langs/nheko_pt_PT.ts b/resources/langs/nheko_pt_PT.ts
index 59221684..e1b86e2c 100644
--- a/resources/langs/nheko_pt_PT.ts
+++ b/resources/langs/nheko_pt_PT.ts
@@ -1390,11 +1390,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
- Selecionar um ficheiro
-
diff --git a/resources/langs/nheko_ro.ts b/resources/langs/nheko_ro.ts
index a90dfda7..59702740 100644
--- a/resources/langs/nheko_ro.ts
+++ b/resources/langs/nheko_ro.ts
@@ -1392,11 +1392,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
-
-
diff --git a/resources/langs/nheko_ru.ts b/resources/langs/nheko_ru.ts
index 27192dd3..444a6f38 100644
--- a/resources/langs/nheko_ru.ts
+++ b/resources/langs/nheko_ru.ts
@@ -1392,11 +1392,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
- Выберите файл
-
diff --git a/resources/langs/nheko_si.ts b/resources/langs/nheko_si.ts
index e4ba7628..2254bf24 100644
--- a/resources/langs/nheko_si.ts
+++ b/resources/langs/nheko_si.ts
@@ -1390,11 +1390,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
-
-
diff --git a/resources/langs/nheko_sr_Latn.ts b/resources/langs/nheko_sr_Latn.ts
index c8b54d06..c77ee762 100644
--- a/resources/langs/nheko_sr_Latn.ts
+++ b/resources/langs/nheko_sr_Latn.ts
@@ -1392,11 +1392,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
-
-
diff --git a/resources/langs/nheko_sv.ts b/resources/langs/nheko_sv.ts
index 9319efa4..ddb64aef 100644
--- a/resources/langs/nheko_sv.ts
+++ b/resources/langs/nheko_sv.ts
@@ -1390,11 +1390,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
- Välj en fil
-
diff --git a/resources/langs/nheko_tr.ts b/resources/langs/nheko_tr.ts
index 71fa01a2..573ff38c 100644
--- a/resources/langs/nheko_tr.ts
+++ b/resources/langs/nheko_tr.ts
@@ -1393,11 +1393,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
-
-
diff --git a/resources/langs/nheko_uk.ts b/resources/langs/nheko_uk.ts
index 99315c8a..72ff3953 100644
--- a/resources/langs/nheko_uk.ts
+++ b/resources/langs/nheko_uk.ts
@@ -1395,11 +1395,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
- Виберіть файл
-
diff --git a/resources/langs/nheko_vi.ts b/resources/langs/nheko_vi.ts
index 6d419b3b..f1525f92 100644
--- a/resources/langs/nheko_vi.ts
+++ b/resources/langs/nheko_vi.ts
@@ -1388,11 +1388,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
-
-
diff --git a/resources/langs/nheko_zh_CN.ts b/resources/langs/nheko_zh_CN.ts
index e6c3d4be..f392c51f 100644
--- a/resources/langs/nheko_zh_CN.ts
+++ b/resources/langs/nheko_zh_CN.ts
@@ -1391,11 +1391,6 @@ You may optionally provide a reason for others to accept your knock:
InputBar
-
-
-
- 选择一个文件
-
diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml
index 4951a9fb..a248114d 100644
--- a/resources/qml/Avatar.qml
+++ b/resources/qml/Avatar.qml
@@ -11,78 +11,67 @@ import im.nheko 1.0
AbstractButton {
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 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
+ width: 48
+
background: Rectangle {
id: bg
+
+ color: palette.alternateBase
radius: Settings.avatarCircles ? height / 2 : height / 8
- color: Nheko.colors.alternateBase
}
Label {
id: label
- enabled: false
-
anchors.fill: parent
- text: TimelineManager.escapeEmoji(displayName ? String.fromCodePoint(displayName.codePointAt(0)) : "")
- textFormat: Text.RichText
+ color: palette.text
+ enabled: false
font.pixelSize: avatar.height / 2
- verticalAlignment: Text.AlignVCenter
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
- color: Nheko.colors.text
}
-
Image {
id: identicon
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
- source: Settings.useIdenticon ? ("image://jdenticon/" + (userid !== "" ? userid : roomid) + "?radius=" + (Settings.avatarCircles ? 100 : 25)) : ""
}
-
Image {
id: img
anchors.fill: parent
asynchronous: true
fillMode: avatar.crop ? Image.PreserveAspectCrop : Image.PreserveAspectFit
- mipmap: true
- smooth: true
- sourceSize.width: avatar.width * Screen.devicePixelRatio
- sourceSize.height: avatar.height * Screen.devicePixelRatio
- source: if (avatar.url.startsWith('image://')) {
+ source: if (avatar.url.startsWith('image://colorimage')) {
+ return avatar.url + "&radius=" + (Settings.avatarCircles ? 100 : 25) + ((avatar.crop) ? "" : "&scale");
+ } else if (avatar.url.startsWith('image://')) {
return avatar.url + "?radius=" + (Settings.avatarCircles ? 100 : 25) + ((avatar.crop) ? "" : "&scale");
} else if (avatar.url.startsWith(':/')) {
- return "image://colorimage/" + avatar.url + "?" + textColor;
+ return "image://colorimage/" + avatar.url + "?" + label.color;
} else {
return "";
}
-
+ sourceSize.height: avatar.height * Screen.devicePixelRatio
+ sourceSize.width: avatar.width * Screen.devicePixelRatio
}
-
Rectangle {
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() {
- switch (Presence.userPresence(userid)) {
+ switch (Presence.userPresence(avatar.userid)) {
case "online":
return Nheko.theme.online;
case "unavailable":
@@ -94,22 +83,28 @@ AbstractButton {
}
}
- Connections {
- target: Presence
+ anchors.bottom: avatar.bottom
+ 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) {
- if (id == userid) onlineIndicator.color = onlineIndicator.updatePresence();
+ if (id == avatar.userid)
+ onlineIndicator.color = onlineIndicator.updatePresence();
}
+
+ target: Presence
}
}
-
- CursorShape {
+ NhekoCursorShape {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
-
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)
}
-
}
diff --git a/resources/qml/ChatPage.qml b/resources/qml/ChatPage.qml
index d4d2b845..2803e97d 100644
--- a/resources/qml/ChatPage.qml
+++ b/resources/qml/ChatPage.qml
@@ -14,19 +14,19 @@ import QtQml 2.15
Rectangle {
id: chatPage
- color: Nheko.colors.window
+ color: palette.window
ColumnLayout {
- spacing: 0
anchors.fill: parent
+ spacing: 0
Rectangle {
id: offlineIndicator
+ Layout.fillWidth: true
+ Layout.preferredHeight: offlineLabel.height + Nheko.paddingMedium
color: Nheko.theme.error
visible: !TimelineManager.isConnected
- Layout.preferredHeight: offlineLabel.height + Nheko.paddingMedium
- Layout.fillWidth: true
z: 1
Label {
@@ -36,18 +36,9 @@ Rectangle {
text: qsTr("No network connection")
}
}
-
AdaptiveLayout {
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() {
if (!singlePageMode)
adaptiveView.pageIndex = 0;
@@ -57,67 +48,67 @@ Rectangle {
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 {
- target: Rooms
function onCurrentRoomChanged() {
adaptiveView.initializePageIndex();
}
- }
+ target: Rooms
+ }
AdaptiveLayoutElement {
id: communityListC
- visible: Settings.groupView
- minimumWidth: communitiesList.avatarSize * 4 + Nheko.paddingMedium * 2
collapsedWidth: communitiesList.avatarSize + 2 * Nheko.paddingMedium
- preferredWidth: Settings.communityListWidth >= minimumWidth ? Settings.communityListWidth : collapsedWidth
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 {
id: communitiesList
collapsed: parent.collapsed
}
-
Binding {
- target: Settings
+ delayed: true
property: 'communityListWidth'
+ restoreMode: Binding.RestoreBindingOrValue
+ target: Settings
value: communityListC.preferredWidth
when: !adaptiveView.singlePageMode
- delayed: true
- restoreMode: Binding.RestoreBindingOrValue
}
-
}
-
AdaptiveLayoutElement {
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
+ 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 {
id: roomlist
- height: adaptiveView.height
collapsed: parent.collapsed
+ height: adaptiveView.height
}
-
Binding {
- target: Settings
+ delayed: true
property: 'roomListWidth'
+ restoreMode: Binding.RestoreBindingOrValue
+ target: Settings
value: roomListC.preferredWidth
when: !adaptiveView.singlePageMode
- delayed: true
- restoreMode: Binding.RestoreBindingOrValue
}
-
}
-
AdaptiveLayoutElement {
id: timlineViewC
@@ -127,25 +118,20 @@ Rectangle {
id: timeline
privacyScreen: privacyScreen
- showBackButton: adaptiveView.singlePageMode
room: Rooms.currentRoom
roomPreview: Rooms.currentRoomPreview.roomid ? Rooms.currentRoomPreview : null
+ showBackButton: adaptiveView.singlePageMode
}
-
}
-
}
-
}
-
PrivacyScreen {
id: privacyScreen
anchors.fill: parent
- visible: Settings.privacyScreen
screenTimeout: Settings.privacyScreenTimeout
timelineRoot: adaptiveView
+ visible: Settings.privacyScreen
windowTarget: MainWindow
}
-
}
diff --git a/resources/qml/CommunitiesList.qml b/resources/qml/CommunitiesList.qml
index ee49ae2d..81f0640e 100644
--- a/resources/qml/CommunitiesList.qml
+++ b/resources/qml/CommunitiesList.qml
@@ -13,19 +13,24 @@ import im.nheko 1.0
Page {
id: communitySidebar
+
//leftPadding: Nheko.paddingSmall
//rightPadding: Nheko.paddingSmall
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 1.6)
property bool collapsed: false
+ background: Rectangle {
+ color: Nheko.theme.sidebarBackground
+ }
+
// HACK: https://bugreports.qt.io/browse/QTBUG-83972, qtwayland cannot auto hide menu
Connections {
function onHideMenu() {
- communityContextMenu.close()
+ communityContextMenu.close();
}
+
target: MainWindow
}
-
ListView {
id: communitiesList
@@ -36,21 +41,161 @@ Page {
ScrollBar.vertical: ScrollBar {
id: scrollbar
+
parent: !collapsed && Settings.scrollbarsInRoomlist ? communitiesList : null
}
+ delegate: ItemDelegate {
+ id: communityItem
- ScrollHelper {
- flickable: parent
- anchors.fill: parent
- enabled: !Settings.mobileMode
+ property color backgroundColor: palette.window
+ property color bubbleBackground: palette.highlight
+ property color bubbleText: palette.highlightedText
+ 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 {
id: communityContextMenu
- property string tagId
property bool hidden
property bool muted
+ property string tagId
function show(id_, hidden_, muted_) {
tagId = id_;
@@ -60,177 +205,19 @@ Page {
}
Platform.MenuItem {
- text: qsTr("Do not show notification counts for this community or tag.")
checkable: true
checked: communityContextMenu.muted
+ text: qsTr("Do not show notification counts for this community or tag.")
+
onTriggered: Communities.toggleTagMute(communityContextMenu.tagId)
}
-
Platform.MenuItem {
- text: qsTr("Hide rooms with this tag or from this community by default.")
checkable: true
checked: communityContextMenu.hidden
+ text: qsTr("Hide rooms with this tag or from this community by default.")
+
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
- }
-
}
diff --git a/resources/qml/Completer.qml b/resources/qml/Completer.qml
index 0a9c41ed..9aebfdf5 100644
--- a/resources/qml/Completer.qml
+++ b/resources/qml/Completer.qml
@@ -11,124 +11,107 @@ import im.nheko 1.0
Control {
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 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 rowSpacing: Nheko.paddingSmall
- property alias count: listView.count
signal completionClicked(string completion)
signal completionSelected(string id)
- function up() {
- if (bottomToTop)
- down_();
- else
- up_();
+ function changeCompleter() {
+ if (completerName) {
+ completer = TimelineManager.completerFor(completerName, completerName == "room" ? "" : (popup.roomId != "" ? popup.roomId : room.roomId));
+ completer.setSearchString("");
+ } 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() {
if (currentIndex > -1 && currentIndex < listView.count)
return completer.completionAt(currentIndex);
else
return null;
}
-
+ function down() {
+ if (bottomToTop)
+ up_();
+ else
+ down_();
+ }
+ function down_() {
+ currentIndex = currentIndex + 1;
+ if (currentIndex >= listView.count)
+ currentIndex = -1;
+ }
function finishCompletion() {
if (popup.completerName == "room")
popup.completionSelected(listView.itemAtIndex(currentIndex).modelData.roomid);
else if (popup.completerName == "user")
popup.completionSelected(listView.itemAtIndex(currentIndex).modelData.userid);
-
}
-
- function changeCompleter() {
- if (completerName) {
- completer = TimelineManager.completerFor(completerName, completerName == "room" ? "" : (popup.roomId != "" ? popup.roomId : room.roomId));
- completer.setSearchString("");
- } else {
- completer = undefined;
- }
- currentIndex = -1
+ function up() {
+ if (bottomToTop)
+ down_();
+ else
+ up_();
+ }
+ function up_() {
+ currentIndex = currentIndex - 1;
+ if (currentIndex == -2)
+ currentIndex = listView.count - 1;
}
- onCompleterNameChanged: changeCompleter()
- onRoomIdChanged: changeCompleter()
bottomPadding: 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 {
id: listView
- // If we have fewer than 7 items, just use the list view's content height.
+ 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.
// 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
// 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!
- 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()
+ implicitHeight: Math.min(contentHeight, 6 * rowSpacing + 7 * (popup.avatarHeight + 2 * rowMargin))
// Broken, see https://bugreports.qt.io/browse/QTBUG-102811
//reuseItems: true
- implicitWidth: listView.contentItem.childrenRect.width
+ implicitWidth: Math.max(listView.contentItem.childrenRect.width, 20)
model: completer
- verticalLayoutDirection: popup.bottomToTop ? ListView.BottomToTop : ListView.TopToBottom
- spacing: rowSpacing
pixelAligned: true
- highlightFollowsCurrentItem: true
-
- displayMarginBeginning: height / 2
- displayMarginEnd: height / 2
+ spacing: rowSpacing
+ verticalLayoutDirection: popup.bottomToTop ? ListView.BottomToTop : ListView.TopToBottom
delegate: Rectangle {
property variant modelData: model
ListView.delayRemove: true
-
- color: model.index == popup.currentIndex ? Nheko.colors.highlight : Nheko.colors.base
- height: chooser.child.implicitHeight + 2 * popup.rowMargin
+ color: model.index == popup.currentIndex ? palette.highlight : palette.base
+ height: (chooser.child?.implicitHeight ?? 0) + 2 * popup.rowMargin
implicitWidth: fullWidth ? ListView.view.width : chooser.child.implicitWidth + 4
MouseArea {
@@ -136,26 +119,27 @@ Control {
anchors.fill: parent
hoverEnabled: true
- onPositionChanged: if (!listView.moving && !deadTimer.running) popup.currentIndex = model.index
+
onClicked: {
- popup.completionClicked(completer.completionAt(model.index));
- if (popup.completerName == "room")
- popup.completionSelected(model.roomid);
- else if (popup.completerName == "user")
- popup.completionSelected(model.userid);
+ popup.completionClicked(completer.completionAt(model.index));
+ if (popup.completerName == "room")
+ popup.completionSelected(model.roomid);
+ else if (popup.completerName == "user")
+ popup.completionSelected(model.userid);
}
+ onPositionChanged: if (!listView.moving && !deadTimer.running)
+ popup.currentIndex = model.index
}
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 {
id: chooser
- roleValue: popup.completerName
anchors.fill: parent
anchors.margins: popup.rowMargin
enabled: false
+ roleValue: popup.completerName
DelegateChoice {
roleValue: "user"
@@ -167,28 +151,23 @@ Control {
spacing: rowSpacing
Avatar {
- height: popup.avatarHeight
- width: popup.avatarWidth
displayName: model.displayName
- userid: model.userid
- url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
enabled: false
+ height: popup.avatarHeight
+ url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
+ userid: model.userid
+ width: popup.avatarWidth
}
-
Label {
+ color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
text: model.displayName
- color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.text
}
-
Label {
+ color: model.index == popup.currentIndex ? palette.highlightedText : palette.buttonText
text: "(" + model.userid + ")"
- color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.buttonText
}
-
}
-
}
-
DelegateChoice {
roleValue: "emoji"
@@ -199,39 +178,33 @@ Control {
spacing: rowSpacing
Label {
- visible: !!model.unicode
- text: model.unicode
- color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.text
+ color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
font: Settings.emojiFont
+ text: model.unicode
+ visible: !!model.unicode
}
-
Avatar {
- visible: !model.unicode
- height: popup.avatarHeight
- width: popup.avatarWidth
+ crop: false
displayName: model.shortcode
+ enabled: false
+ height: popup.avatarHeight
//userid: model.shortcode
url: (model.url ? model.url : "").replace("mxc://", "image://MxcImage/")
- enabled: false
- crop: false
+ visible: !model.unicode
+ width: popup.avatarWidth
}
-
Label {
Layout.leftMargin: Nheko.paddingSmall
Layout.rightMargin: Nheko.paddingSmall
+ color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
text: model.shortcode
- color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.text
}
-
Label {
+ color: model.index == popup.currentIndex ? palette.highlightedText : palette.buttonText
text: "(" + model.packname + ")"
- color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.buttonText
}
-
}
-
}
-
DelegateChoice {
roleValue: "command"
@@ -242,20 +215,16 @@ Control {
spacing: rowSpacing
Label {
- text: model.name
- color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.text
+ color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
font.bold: true
+ text: model.name
}
-
Label {
+ color: model.index == popup.currentIndex ? palette.highlightedText : palette.buttonText
text: model.description
- color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.buttonText
}
-
}
-
}
-
DelegateChoice {
roleValue: "room"
@@ -266,26 +235,22 @@ Control {
spacing: rowSpacing
Avatar {
- height: popup.avatarHeight
- width: popup.avatarWidth
displayName: model.roomName
+ enabled: false
+ height: popup.avatarHeight
roomid: model.roomid
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
- enabled: false
+ width: popup.avatarWidth
}
-
Label {
- text: model.roomName
- font.pixelSize: popup.avatarHeight * 0.5
- color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.text
+ color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
font.italic: model.isTombstoned
+ font.pixelSize: popup.avatarHeight * 0.5
+ text: model.roomName
textFormat: Text.RichText
}
-
}
-
}
-
DelegateChoice {
roleValue: "roomAliases"
@@ -296,41 +261,38 @@ Control {
spacing: rowSpacing
Avatar {
- height: popup.avatarHeight
- width: popup.avatarWidth
displayName: model.roomName
+ enabled: false
+ height: popup.avatarHeight
roomid: model.roomid
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
- enabled: false
+ width: popup.avatarWidth
}
-
Label {
- text: model.roomName
- color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.text
+ color: model.index == popup.currentIndex ? palette.highlightedText : palette.text
font.italic: model.isTombstoned
+ text: model.roomName
textFormat: Text.RichText
}
-
Label {
+ color: model.index == popup.currentIndex ? palette.highlightedText : palette.buttonText
text: "(" + model.roomAlias + ")"
- color: model.index == popup.currentIndex ? Nheko.colors.highlightedText : Nheko.colors.buttonText
textFormat: Text.RichText
}
-
}
-
}
-
}
-
}
+ onContentYChanged: deadTimer.restart()
+
+ Timer {
+ id: deadTimer
+
+ interval: 50
+ }
}
-
- background: Rectangle {
- color: Nheko.colors.base
- border.color: Nheko.colors.mid
- }
-
+ onCompleterNameChanged: changeCompleter()
+ onRoomIdChanged: changeCompleter()
}
diff --git a/resources/qml/ElidedLabel.qml b/resources/qml/ElidedLabel.qml
index 180259b1..153d7c33 100644
--- a/resources/qml/ElidedLabel.qml
+++ b/resources/qml/ElidedLabel.qml
@@ -9,21 +9,20 @@ import im.nheko 1.0
Label {
id: root
- property alias fullText: metrics.text
property alias elideWidth: metrics.elideWidth
+ property alias fullText: metrics.text
property int fullTextWidth: Math.ceil(metrics.advanceWidth)
- color: Nheko.colors.text
- text: (textFormat == Text.PlainText) ? metrics.elidedText : TimelineManager.escapeEmoji(metrics.elidedText)
- maximumLineCount: 1
+ color: palette.text
elide: Text.ElideRight
+ maximumLineCount: 1
+ text: (textFormat == Text.PlainText) ? metrics.elidedText : TimelineManager.escapeEmoji(metrics.elidedText)
textFormat: Text.PlainText
TextMetrics {
id: metrics
- font.pointSize: root.font.pointSize
elide: Text.ElideRight
+ font.pointSize: root.font.pointSize
}
-
}
diff --git a/resources/qml/EncryptionIndicator.qml b/resources/qml/EncryptionIndicator.qml
index 5338b6be..fb9dc7b5 100644
--- a/resources/qml/EncryptionIndicator.qml
+++ b/resources/qml/EncryptionIndicator.qml
@@ -11,51 +11,29 @@ Image {
id: stateImg
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 string sourceUrl: {
if (!encrypted)
- return "image://colorimage/" + unencryptedIcon + "?";
-
+ return "image://colorimage/" + unencryptedIcon + "?";
switch (trust) {
- case Crypto.Verified:
+ case Crypto.Verified:
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?";
- case Crypto.Unverified:
+ case Crypto.Unverified:
return "image://colorimage/:/icons/icons/ui/shield-filled-exclamation-mark.svg?";
- default:
+ default:
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: {
if (!encrypted)
return qsTr("This message is not encrypted!");
-
switch (trust) {
case Crypto.Verified:
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.");
}
}
+ 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 {
id: ma
- }
+ }
}
diff --git a/resources/qml/ForwardCompleter.qml b/resources/qml/ForwardCompleter.qml
index 4ab23a4f..0174e0f6 100644
--- a/resources/qml/ForwardCompleter.qml
+++ b/resources/qml/ForwardCompleter.qml
@@ -16,14 +16,24 @@ Popup {
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)
y: Math.round(parent.height / 4)
- modal: true
- palette: Nheko.colors
- parent: Overlay.overlay
- width: timelineRoot.width * 0.8
- leftPadding: 10
- rightPadding: 10
+
+ Overlay.modal: Rectangle {
+ color: Qt.rgba(palette.window.r, palette.window.g, palette.window.b, 0.7)
+ }
+ background: Rectangle {
+ color: palette.window
+ }
+
onOpened: {
roomTextInput.forceActiveFocus();
}
@@ -36,46 +46,40 @@ Popup {
Label {
id: titleLabel
- text: qsTr("Forward Message")
- font.bold: true
bottomPadding: 10
- color: Nheko.colors.text
+ color: palette.text
+ font.bold: true
+ text: qsTr("Forward Message")
}
-
Reply {
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 ?? ""
body: modelData.body ?? ""
- formattedBody: modelData.formattedBody ?? ""
+ encryptionError: modelData.encryptionError ?? ""
eventId: modelData.eventId ?? ""
filename: modelData.filename ?? ""
filesize: modelData.filesize ?? ""
+ formattedBody: modelData.formattedBody ?? ""
+ isOnlyEmoji: modelData.isOnlyEmoji ?? false
+ originalWidth: modelData.originalWidth ?? 0
proportionalHeight: modelData.proportionalHeight ?? 1
type: modelData.type ?? MtxEvent.UnknownMessage
typeString: modelData.typeString ?? ""
url: modelData.url ?? ""
- originalWidth: modelData.originalWidth ?? 0
- isOnlyEmoji: modelData.isOnlyEmoji ?? false
+ userColor: TimelineManager.userColor(modelData.userId, palette.window)
userId: modelData.userId ?? ""
userName: modelData.userName ?? ""
- encryptionError: modelData.encryptionError ?? ""
+ width: parent.width
}
-
MatrixTextField {
id: roomTextInput
+ color: palette.text
width: forwardMessagePopup.width - forwardMessagePopup.leftPadding * 2
- color: Nheko.colors.text
- onTextEdited: {
- completerPopup.completer.searchString = text;
- }
+
Keys.onPressed: {
if (event.key == Qt.Key_Up || event.key == Qt.Key_Backtab) {
event.accepted = true;
@@ -91,43 +95,32 @@ Popup {
event.accepted = true;
}
}
+ onTextEdited: {
+ completerPopup.completer.searchString = text;
+ }
}
-
Completer {
id: completerPopup
- width: forwardMessagePopup.width - forwardMessagePopup.leftPadding * 2
- completerName: "room"
- fullWidth: true
- centerRowContent: false
avatarHeight: 24
avatarWidth: 24
bottomToTop: false
+ centerRowContent: false
+ completerName: "room"
+ fullWidth: true
+ width: forwardMessagePopup.width - forwardMessagePopup.leftPadding * 2
}
-
}
-
Connections {
function onCompletionSelected(id) {
room.forwardMessage(messageContextMenu.eventId, id);
forwardMessagePopup.close();
}
-
function onCountChanged() {
if (completerPopup.count > 0 && (completerPopup.currentIndex < 0 || completerPopup.currentIndex >= completerPopup.count))
completerPopup.currentIndex = 0;
-
}
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)
- }
-
}
diff --git a/resources/qml/ImageButton.qml b/resources/qml/ImageButton.qml
index 547b4a12..ddc0b7d8 100644
--- a/resources/qml/ImageButton.qml
+++ b/resources/qml/ImageButton.qml
@@ -2,6 +2,7 @@
//
// SPDX-License-Identifier: GPL-3.0-or-later
+pragma ComponentBehavior: Bound
import "./ui"
import QtQuick 2.3
import QtQuick.Controls 2.3
@@ -10,38 +11,35 @@ import im.nheko 1.0 // for cursor shape
AbstractButton {
id: button
- property alias cursor: mouseArea.cursorShape
- property string image: undefined
- property color highlightColor: Nheko.colors.highlight
- property color buttonTextColor: Nheko.colors.buttonText
+ property color buttonTextColor: palette.buttonText
property bool changeColorOnHover: true
+ property alias cursor: mouseArea.cursorShape
+ property color highlightColor: palette.highlight
+ property string image: undefined
property bool ripple: true
focusPolicy: Qt.NoFocus
- width: 16
height: 16
+ width: 16
Image {
id: buttonImg
// Workaround, can't get icon.source working for now...
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.width: button.width
- fillMode: Image.PreserveAspectFit
}
-
- CursorShape {
+ NhekoCursorShape {
id: mouseArea
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
-
Ripple {
+ color: Qt.rgba(button.buttonTextColor.r, button.buttonTextColor.g, button.buttonTextColor.b, 0.5)
enabled: button.ripple
- color: Qt.rgba(buttonTextColor.r, buttonTextColor.g, buttonTextColor.b, 0.5)
}
-
}
diff --git a/resources/qml/MatrixText.qml b/resources/qml/MatrixText.qml
index 7956f0b6..94b8bb98 100644
--- a/resources/qml/MatrixText.qml
+++ b/resources/qml/MatrixText.qml
@@ -11,28 +11,42 @@ TextEdit {
property alias cursorShape: cs.cursorShape
- textFormat: TextEdit.RichText
- readOnly: true
- focus: false
- wrapMode: Text.Wrap
- selectByMouse: !Settings.mobileMode
+ //leftInset: 0
+ //bottomInset: 0
+ //rightInset: 0
+ //topInset: 0
+ //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!
//enabled: selectByMouse
- color: Nheko.colors.text
- onLinkActivated: Nheko.openLink(link)
- ToolTip.visible: hoveredLink || false
- ToolTip.text: hoveredLink
+ color: palette.text
+ focus: false
+ readOnly: true
+ selectByMouse: !Settings.mobileMode
+ textFormat: TextEdit.RichText
+ wrapMode: Text.Wrap
+
// Setting a tooltip delay makes the hover text empty .-.
//ToolTip.delay: Nheko.tooltipDelay
Component.onCompleted: {
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
anchors.fill: parent
cursorShape: hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
-
}
diff --git a/resources/qml/MatrixTextField.qml b/resources/qml/MatrixTextField.qml
index 74cacf33..7209a5aa 100644
--- a/resources/qml/MatrixTextField.qml
+++ b/resources/qml/MatrixTextField.qml
@@ -7,68 +7,63 @@ import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import im.nheko 1.0
-
ColumnLayout {
id: c
- property color backgroundColor: Nheko.colors.base
+
+ property color backgroundColor: palette.base
property alias color: labelC.color
- property alias textPadding: input.padding
- property alias text: input.text
+ property alias echoMode: input.echoMode
+ property alias font: input.font
+ property var hasClear: false
property alias label: labelC.text
property alias placeholderText: input.placeholderText
- property alias font: input.font
- property alias echoMode: input.echoMode
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 editingFinished
-
- function forceActiveFocus() {
- input.forceActiveFocus();
- }
+ signal textEdited
function clear() {
input.clear();
}
+ function forceActiveFocus() {
+ input.forceActiveFocus();
+ }
ToolTip.delay: Nheko.tooltipDelay
ToolTip.visible: hover.hovered
-
spacing: 0
- Item {
- Layout.fillWidth: true
- Layout.preferredHeight: labelC.contentHeight
- Layout.margins: input.padding
- Layout.bottomMargin: Nheko.paddingSmall
- visible: labelC.text
+ onTextChanged: timer.restart()
+ 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
Label {
id: labelC
- y: contentHeight + input.padding + Nheko.paddingSmall
+ color: palette.text
enabled: false
-
- palette: Nheko.colors
- color: Nheko.colors.text
+ font.letterSpacing: input.font.pixelSize * 0.02
font.pixelSize: input.font.pixelSize
font.weight: Font.DemiBold
- font.letterSpacing: input.font.pixelSize * 0.02
- width: parent.width
-
state: labelC.text && (input.activeFocus == true || input.text) ? "focused" : ""
+ width: parent.width
+ y: contentHeight + input.padding + Nheko.paddingSmall
states: State {
name: "focused"
@@ -77,51 +72,40 @@ ColumnLayout {
target: labelC
y: 0
}
-
PropertyChanges {
- target: input
opacity: 1
+ target: input
}
-
}
-
transitions: Transition {
from: ""
- to: "focused"
reversible: true
+ to: "focused"
NumberAnimation {
- target: labelC
+ alwaysRunToEnd: true
+ duration: 210
+ easing.type: Easing.InCubic
properties: "y"
- duration: 210
- easing.type: Easing.InCubic
- alwaysRunToEnd: true
+ target: labelC
}
-
NumberAnimation {
- target: input
- properties: "opacity"
+ alwaysRunToEnd: true
duration: 210
easing.type: Easing.InCubic
- alwaysRunToEnd: true
+ properties: "opacity"
+ target: input
}
-
}
}
}
-
TextField {
id: input
+
Layout.fillWidth: true
-
- palette: Nheko.colors
color: labelC.color
- opacity: labelC.text ? 0 : 1
focus: true
-
- onTextEdited: c.textEdited()
- onAccepted: c.accepted()
- onEditingFinished: c.editingFinished()
+ opacity: labelC.text ? 0 : 1
background: Rectangle {
id: backgroundRect
@@ -129,44 +113,46 @@ ColumnLayout {
color: labelC.text ? "transparent" : backgroundColor
}
+ onAccepted: c.accepted()
+ onEditingFinished: c.editingFinished()
+ onTextEdited: c.textEdited()
+
ImageButton {
id: clearText
+ focusPolicy: Qt.NoFocus
+ hoverEnabled: true
+ image: ":/icons/icons/ui/round-remove-button.svg"
visible: c.hasClear && searchField.text !== ''
- image: ":/icons/icons/ui/round-remove-button.svg"
- focusPolicy: Qt.NoFocus
onClicked: {
- searchField.clear()
+ searchField.clear();
topBar.searchString = "";
}
- hoverEnabled: true
+
anchors {
- top: parent.top
bottom: parent.bottom
right: parent.right
rightMargin: Nheko.paddingSmall
+ top: parent.top
}
}
-
}
-
Rectangle {
id: blueBar
Layout.fillWidth: true
-
- color: Nheko.colors.highlight
+ color: palette.highlight
height: 1
Rectangle {
id: blackBar
- anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
- height: parent.height*2
+ anchors.top: parent.top
+ color: palette.text
+ height: parent.height * 2
width: 0
- color: Nheko.colors.text
states: State {
name: "focused"
@@ -176,31 +162,25 @@ ColumnLayout {
target: blackBar
width: blueBar.width
}
-
}
-
transitions: Transition {
from: ""
- to: "focused"
reversible: true
-
+ to: "focused"
NumberAnimation {
- target: blackBar
- properties: "width"
+ alwaysRunToEnd: true
duration: 310
easing.type: Easing.InCubic
- alwaysRunToEnd: true
+ properties: "width"
+ target: blackBar
}
-
}
-
}
-
}
-
HoverHandler {
id: hover
+
enabled: c.ToolTip.text
}
}
diff --git a/resources/qml/MessageInput.qml b/resources/qml/MessageInput.qml
index 8e72f458..e196b06d 100644
--- a/resources/qml/MessageInput.qml
+++ b/resources/qml/MessageInput.qml
@@ -14,60 +14,54 @@ import im.nheko 1.0
Rectangle {
id: inputBar
+ property bool showAllButtons: width > 450 || (messageInput.length == 0 && !messageInput.inputMethodComposing)
readonly property string text: messageInput.text
- color: Nheko.colors.window
Layout.fillWidth: true
- Layout.preferredHeight: row.implicitHeight
Layout.minimumHeight: 40
- property bool showAllButtons: width > 450 || (messageInput.length == 0 && !messageInput.inputMethodComposing)
-
+ Layout.preferredHeight: row.implicitHeight
+ color: palette.window
Component {
id: placeCallDialog
PlaceCall {
}
-
}
-
Component {
id: screenShareDialog
ScreenShare {
}
-
}
-
RowLayout {
id: row
- visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
anchors.fill: parent
spacing: 0
+ visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
ImageButton {
- visible: CallManager.callsSupported && showAllButtons
- opacity: (CallManager.haveCallInvite || CallManager.isOnCallOnOtherDevice) ? 0.3 : 1
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
+ 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: {
if (room) {
if (CallManager.haveCallInvite) {
- return ;
+ return;
} else if (CallManager.isOnCall) {
CallManager.hangUp();
- }
- else if(CallManager.isOnCallOnOtherDevice) {
+ } else if (CallManager.isOnCallOnOtherDevice) {
return;
- }
- else {
+ } else {
var dialog = placeCallDialog.createObject(timelineRoot);
dialog.open();
timelineRoot.destroyOnClose(dialog);
@@ -75,22 +69,22 @@ Rectangle {
}
}
}
-
ImageButton {
- visible: showAllButtons
Layout.alignment: Qt.AlignBottom
- hoverEnabled: true
- width: 22
- height: 22
- image: ":/icons/icons/ui/attach.svg"
Layout.margins: 8
- onClicked: room.input.openFileSelection()
- ToolTip.visible: hovered
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 {
anchors.fill: parent
- color: Nheko.colors.window
+ color: palette.window
visible: room && room.input.uploading
Spinner {
@@ -98,112 +92,67 @@ Rectangle {
height: parent.height / 2
running: parent.visible
}
-
}
-
}
-
ScrollView {
id: textInput
Layout.alignment: Qt.AlignVCenter
+ Layout.fillWidth: true
Layout.maximumHeight: Window.height / 4
Layout.minimumHeight: fontMetrics.lineSpacing
Layout.preferredHeight: contentHeight
- Layout.fillWidth: true
-
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
-
contentWidth: availableWidth
TextArea {
id: messageInput
property int completerTriggeredAt: 0
+ property string lastChar
function insertCompletion(completion) {
messageInput.remove(completerTriggeredAt, cursorPosition);
messageInput.insert(cursorPosition, completion);
}
-
function openCompleter(pos, type) {
- if (popup.opened) return;
+ if (popup.opened)
+ return;
completerTriggeredAt = pos;
completer.completerName = type;
popup.open();
- completer.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition)+messageInput.preeditText);
+ completer.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition) + messageInput.preeditText);
}
-
function positionCursorAtEnd() {
cursorPosition = messageInput.length;
}
-
function positionCursorAtStart() {
cursorPosition = 0;
}
- selectByMouse: true
- 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
+ background: null
bottomPadding: 8
- leftPadding: inputBar.showAllButtons? 0 : 8
+ color: palette.text
focus: true
- property string lastChar
- 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");
- }
- }
- onCursorPositionChanged: {
- if (!room)
- return ;
+ leftPadding: inputBar.showAllButtons ? 0 : 8
+ padding: 0
+ placeholderText: qsTr("Write a message...")
+ placeholderTextColor: palette.buttonText
+ selectByMouse: true
+ topPadding: 8
+ verticalAlignment: TextEdit.AlignVCenter
+ width: textInput.width
+ wrapMode: TextEdit.Wrap
- 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);
- }
- 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: {
+ Keys.onPressed: event => {
if (event.matches(StandardKey.Paste)) {
event.accepted = room.input.tryPasteAttachment(false);
} else if (event.key == Qt.Key_Space) {
// close popup if user enters space after colon
if (cursorPosition == completerTriggeredAt + 1)
popup.close();
-
if (popup.opened && completer.count <= 0)
popup.close();
-
} else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_U) {
messageInput.clear();
} else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_P) {
@@ -218,8 +167,8 @@ Rectangle {
completer.completerName = "";
popup.close();
} 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")) {
room.input.send();
event.accepted = true;
@@ -253,16 +202,16 @@ Rectangle {
console.log('"' + t + '"');
if (t == '@') {
messageInput.openCompleter(pos, "user");
- return ;
+ return;
} else if (t == ' ' || t == '\t') {
messageInput.openCompleter(pos + 1, "user");
- return ;
+ return;
} else if (t == ':') {
messageInput.openCompleter(pos, "emoji");
- return ;
+ return;
} else if (t == '~') {
messageInput.openCompleter(pos, "customEmoji");
- return ;
+ return;
}
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 {
function onRoomChanged() {
messageInput.clear();
if (room)
messageInput.append(room.input.text);
-
completer.completerName = "";
messageInput.forceActiveFocus();
}
target: timelineView
}
-
Connections {
function onCompletionClicked(completion) {
messageInput.insertCompletion(completion);
@@ -334,43 +315,39 @@ Rectangle {
target: completer
}
-
Popup {
id: popup
+ background: null
+ padding: 0
x: messageInput.positionToRectangle(messageInput.completerTriggeredAt).x
y: messageInput.positionToRectangle(messageInput.completerTriggeredAt).y - height
- background: null
- padding: 0
+ enter: Transition {
+ NumberAnimation {
+ duration: 100
+ from: 0
+ property: "opacity"
+ to: 1
+ }
+ }
+ exit: Transition {
+ NumberAnimation {
+ duration: 100
+ from: 1
+ property: "opacity"
+ to: 0
+ }
+ }
Completer {
- anchors.fill: parent
id: completer
+
+ anchors.fill: parent
rowMargin: 2
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 {
function onTextChanged(newText) {
messageInput.text = newText;
@@ -380,16 +357,13 @@ Rectangle {
ignoreUnknownSignals: true
target: room ? room.input : null
}
-
Connections {
- function onReplyChanged() {
- messageInput.forceActiveFocus();
- }
-
function onEditChanged() {
messageInput.forceActiveFocus();
}
-
+ function onReplyChanged() {
+ messageInput.forceActiveFocus();
+ }
function onThreadChanged() {
messageInput.forceActiveFocus();
}
@@ -397,7 +371,6 @@ Rectangle {
ignoreUnknownSignals: true
target: room
}
-
Connections {
function onFocusInput() {
messageInput.forceActiveFocus();
@@ -405,91 +378,82 @@ Rectangle {
target: TimelineManager
}
-
MouseArea {
+ acceptedButtons: Qt.MiddleButton
// workaround for wrong cursor shape on some platforms
anchors.fill: parent
- acceptedButtons: Qt.MiddleButton
cursorShape: Qt.IBeamCursor
- onPressed: (mouse) => mouse.accepted = room.input.tryPasteAttachment(true)
+
+ onPressed: mouse => mouse.accepted = room.input.tryPasteAttachment(true)
}
-
}
-
}
-
ImageButton {
id: stickerButton
- visible: showAllButtons
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.margins: 8
- hoverEnabled: true
- width: 22
- height: 22
- image: ":/icons/icons/ui/sticky-note-solid.svg"
- ToolTip.visible: hovered
ToolTip.text: qsTr("Stickers")
- onClicked: stickerPopup.visible ? stickerPopup.close() : stickerPopup.show(stickerButton, room.roomId, function(row) {
- room.input.sticker(row);
- TimelineManager.focusMessageInput();
- })
+ ToolTip.visible: hovered
+ height: 22
+ 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 {
id: stickerPopup
- colors: Nheko.colors
emoji: false
}
-
}
-
ImageButton {
id: emojiButton
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.margins: 8
- hoverEnabled: true
- width: 22
- height: 22
- image: ":/icons/icons/ui/smile.svg"
- ToolTip.visible: hovered
ToolTip.text: qsTr("Emoji")
- onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(emojiButton, room.roomId, function(plaintext, markdown) {
- messageInput.insert(messageInput.cursorPosition, markdown);
- TimelineManager.focusMessageInput();
- })
+ ToolTip.visible: hovered
+ height: 22
+ 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 {
id: emojiPopup
- colors: Nheko.colors
emoji: true
}
}
-
ImageButton {
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.margins: 8
- hoverEnabled: true
- width: 22
- height: 22
- image: ":/icons/icons/ui/send.svg"
Layout.rightMargin: 8
- ToolTip.visible: hovered
ToolTip.text: qsTr("Send")
+ ToolTip.visible: hovered
+ height: 22
+ hoverEnabled: true
+ image: ":/icons/icons/ui/send.svg"
+ width: 22
+
onClicked: {
room.input.send();
}
}
-
}
-
Text {
anchors.centerIn: parent
- visible: room ? (!room.permissions.canSend(MtxEvent.TextMessage)) : false
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
}
-
}
diff --git a/resources/qml/MessageInputWarning.qml b/resources/qml/MessageInputWarning.qml
index 9b0b0907..4d5578b3 100644
--- a/resources/qml/MessageInputWarning.qml
+++ b/resources/qml/MessageInputWarning.qml
@@ -10,38 +10,35 @@ import im.nheko 1.0
Rectangle {
id: warningRoot
- required property string text
property color bubbleColor: Nheko.theme.error
+ required property string text
- implicitHeight: visible ? warningDisplay.implicitHeight + 4 * Nheko.paddingSmall : 0
- height: implicitHeight
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 {
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.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
Label {
id: warningDisplay
anchors.left: parent.left
- anchors.verticalCenter: parent.verticalCenter
anchors.margins: Nheko.paddingSmall
- color: Nheko.colors.text
+ anchors.verticalCenter: parent.verticalCenter
text: warningRoot.text
textFormat: Text.PlainText
}
-
}
-
}
diff --git a/resources/qml/MessageView.qml b/resources/qml/MessageView.qml
index 206b9a17..af3a3371 100644
--- a/resources/qml/MessageView.qml
+++ b/resources/qml/MessageView.qml
@@ -14,89 +14,270 @@ import QtQuick.Layouts 1.2
import QtQuick.Window 2.13
import im.nheko 1.0
-
Item {
id: chatRoot
- property int padding: Nheko.paddingMedium
property int availableWidth: width
-
+ property int padding: Nheko.paddingMedium
property string searchString: ""
// HACK: https://bugreports.qt.io/browse/QTBUG-83972, qtwayland cannot auto hide menu
Connections {
function onHideMenu() {
- messageContextMenu.close()
- replyContextMenu.close()
+ messageContextMenu.close();
+ replyContextMenu.close();
}
+
target: MainWindow
}
-
ScrollBar {
id: scrollbar
- parent: chat.parent
- anchors.top: parent.top
- anchors.right: parent.right
+
anchors.bottom: parent.bottom
+ anchors.right: parent.right
+ anchors.top: parent.top
+ parent: chat.parent
}
ListView {
id: chat
- anchors.fill: parent
-
- property int delegateMaxWidth: ((Settings.timelineMaxWidth > 100 && Settings.timelineMaxWidth < chatRoot.availableWidth) ? Settings.timelineMaxWidth : chatRoot.availableWidth) - chatRoot.padding * 2 - (scrollbar.interactive? scrollbar.width : 0)
-
+ property int delegateMaxWidth: ((Settings.timelineMaxWidth > 100 && Settings.timelineMaxWidth < chatRoot.availableWidth) ? Settings.timelineMaxWidth : chatRoot.availableWidth) - chatRoot.padding * 2 - (scrollbar.interactive ? scrollbar.width : 0)
readonly property alias filteringInProgress: filteredTimeline.filteringInProgress
- displayMarginBeginning: height / 2
- displayMarginEnd: height / 2
-
- TimelineFilter {
- id: filteredTimeline
- source: room
- filterByThread: room ? room.thread : ""
- filterByContent: chatRoot.searchString
- }
-
- model: (filteredTimeline.filterByThread || filteredTimeline.filterByContent) ? filteredTimeline : room
+ ScrollBar.vertical: scrollbar
+ anchors.fill: parent
+ anchors.rightMargin: scrollbar.interactive ? scrollbar.width : 0
// reuseItems still has a few bugs, see https://bugreports.qt.io/browse/QTBUG-95105 https://bugreports.qt.io/browse/QTBUG-95107
//onModelChanged: if (room) room.sendReset()
//reuseItems: true
boundsBehavior: Flickable.StopAtBounds
+ displayMarginBeginning: height / 2
+ displayMarginEnd: height / 2
+ model: (filteredTimeline.filterByThread || filteredTimeline.filterByContent) ? filteredTimeline : room
//pixelAligned: true
spacing: 2
verticalLayoutDirection: ListView.BottomToTop
- onCountChanged: {
- // Mark timeline as read
- if (atYEnd && room) model.currentIndex = 0;
+
+ delegate: Item {
+ id: wrapper
+
+ required property string blurhash
+ required property string body
+ required property string callType
+ required property var day
+ required property string duration
+ required property int encryptionError
+ required property string eventId
+ required property string filename
+ required property string filesize
+ required property string formattedBody
+ required property int index
+ required property bool isEditable
+ required property bool isEdited
+ required property bool isEncrypted
+ required property bool isOnlyEmoji
+ required property bool isSender
+ required property bool isStateEvent
+ required property int notificationlevel
+ required property int originalWidth
+ property var previousMessageDay: (index + 1) >= chat.count ? 0 : chat.model.dataByIndex(index + 1, Room.Day)
+ property bool previousMessageIsStateEvent: (index + 1) >= chat.count ? true : chat.model.dataByIndex(index + 1, Room.IsStateEvent)
+ property string previousMessageUserId: (index + 1) >= chat.count ? "" : chat.model.dataByIndex(index + 1, Room.UserId)
+ required property double proportionalHeight
+ required property var reactions
+ required property int relatedEventCacheBuster
+ required property string replyTo
+ required property string roomName
+ required property string roomTopic
+ property bool scrolledToThis: eventId === room.scrollTarget && (y + height > chat.y + chat.contentY && y < chat.y + chat.height + chat.contentY)
+ required property int status
+ 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 userName
+
+ ListView.delayRemove: true
+ anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
+ height: (section.item?.height ?? 0) + timelinerow.height
+ width: chat.delegateMaxWidth
+
+ Loader {
+ id: section
+
+ property var day: wrapper.day
+ property bool isSender: wrapper.isSender
+ property bool isStateEvent: wrapper.isStateEvent
+ property int parentWidth: parent.width
+ property var previousMessageDay: wrapper.previousMessageDay
+ property bool previousMessageIsStateEvent: wrapper.previousMessageIsStateEvent
+ property string previousMessageUserId: wrapper.previousMessageUserId
+ property date timestamp: wrapper.timestamp
+ property string userId: wrapper.userId
+ property string userName: wrapper.userName
+
+ active: previousMessageUserId !== userId || previousMessageDay !== day || previousMessageIsStateEvent !== isStateEvent
+ //asynchronous: true
+ sourceComponent: sectionHeader
+ visible: status == Loader.Ready
+ z: 4
+ }
+ TimelineRow {
+ id: timelinerow
+
+ blurhash: wrapper.blurhash
+ body: wrapper.body
+ callType: wrapper.callType
+ duration: wrapper.duration
+ encryptionError: wrapper.encryptionError
+ eventId: chat.model, wrapper.eventId
+ filename: wrapper.filename
+ filesize: wrapper.filesize
+ formattedBody: wrapper.formattedBody
+ index: wrapper.index
+ isEditable: wrapper.isEditable
+ isEdited: wrapper.isEdited
+ isEncrypted: wrapper.isEncrypted
+ isOnlyEmoji: wrapper.isOnlyEmoji
+ isSender: wrapper.isSender
+ isStateEvent: wrapper.isStateEvent
+ notificationlevel: wrapper.notificationlevel
+ originalWidth: wrapper.originalWidth
+ proportionalHeight: wrapper.proportionalHeight
+ reactions: wrapper.reactions
+ relatedEventCacheBuster: wrapper.relatedEventCacheBuster
+ replyTo: wrapper.replyTo
+ roomName: wrapper.roomName
+ roomTopic: wrapper.roomTopic
+ status: wrapper.status
+ threadId: wrapper.threadId
+ thumbnailUrl: wrapper.thumbnailUrl
+ timestamp: wrapper.timestamp
+ trustlevel: wrapper.trustlevel
+ type: chat.model, wrapper.type
+ typeString: wrapper.typeString
+ url: wrapper.url
+ userId: wrapper.userId
+ userName: wrapper.userName
+ width: wrapper.width
+ y: section.visible && section.active ? section.y + section.height : 0
+
+ background: Rectangle {
+ id: scrollHighlight
+
+ color: palette.highlight
+ enabled: false
+ opacity: 0
+ visible: true
+ z: 1
+
+ states: State {
+ name: "revealed"
+ when: wrapper.scrolledToThis
+ }
+ transitions: Transition {
+ from: ""
+ to: "revealed"
+
+ SequentialAnimation {
+ PropertyAnimation {
+ duration: 500
+ easing.type: Easing.InOutQuad
+ from: 0
+ properties: "opacity"
+ target: scrollHighlight
+ to: 1
+ }
+ PropertyAnimation {
+ duration: 500
+ easing.type: Easing.InOutQuad
+ from: 1
+ properties: "opacity"
+ target: scrollHighlight
+ to: 0
+ }
+ ScriptAction {
+ script: room.eventShown()
+ }
+ }
+ }
+ }
+
+ onHoveredChanged: {
+ if (!Settings.mobileMode && hovered) {
+ if (!messageActions.hovered) {
+ messageActions.attached = timelinerow;
+ messageActions.model = timelinerow;
+ }
+ }
+ }
+ }
+ Connections {
+ function onMovementEnded() {
+ if (y + height + 2 * chat.spacing > chat.contentY + chat.height && y < chat.contentY + chat.height)
+ chat.model.currentIndex = index;
+ }
+
+ target: chat
+ }
+ }
+ footer: Item {
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.margins: Nheko.paddingLarge
+ // hacky, but works
+ height: loadingSpinner.height + 2 * Nheko.paddingLarge
+ visible: (room && room.paginationInProgress) || chat.filteringInProgress
+
+ Spinner {
+ id: loadingSpinner
+
+ anchors.centerIn: parent
+ anchors.margins: Nheko.paddingLarge
+ foreground: palette.mid
+ running: (room && room.paginationInProgress) || chat.filteringInProgress
+ z: 3
+ }
}
- ScrollBar.vertical: scrollbar
+ Window.onActiveChanged: readTimer.running = Window.active
+ onCountChanged: {
+ // Mark timeline as read
+ if (atYEnd && room)
+ model.currentIndex = 0;
+ }
- anchors.rightMargin: scrollbar.interactive? scrollbar.width : 0
+ TimelineFilter {
+ id: filteredTimeline
+ filterByContent: chatRoot.searchString
+ filterByThread: room ? room.thread : ""
+ source: room
+ }
Control {
id: messageActions
property Item attached: null
- property alias model: row.model
// use comma to update on scroll
property var attachedPos: chat.contentY, attached ? chat.mapFromItem(attached, attached ? attached.width - width : 0, -height) : null
- padding: Nheko.paddingSmall
+ property alias model: row.model
hoverEnabled: true
+ padding: Nheko.paddingSmall
visible: Settings.buttonsInTimeline && !!attached && (attached.hovered || hovered)
x: attached ? attachedPos.x : 0
y: attached ? attachedPos.y + Nheko.paddingSmall : 0
z: 10
background: Rectangle {
- color: Nheko.colors.window
- border.color: Nheko.colors.buttonText
+ border.color: palette.buttonText
border.width: 1
+ color: palette.window
radius: padding
}
-
contentItem: RowLayout {
id: row
@@ -106,139 +287,171 @@ Item {
Repeater {
model: Settings.recentReactions
+ visible: room ? room.permissions.canSend(MtxEvent.Reaction) : false
- delegate: TextButton {
+ delegate: AbstractButton {
+ id: button
+
+ property color buttonTextColor: palette.buttonText
+ property color highlightColor: palette.highlight
required property string modelData
+ property bool showImage: modelData.startsWith("mxc://")
- visible: room ? room.permissions.canSend(MtxEvent.Reaction) : false
+ //Layout.preferredHeight: fontMetrics.height
+ Layout.alignment: Qt.AlignBottom
+ focusPolicy: Qt.NoFocus
+ height: showImage ? 16 : buttonText.implicitHeight
+ implicitHeight: showImage ? 16 : buttonText.implicitHeight
+ implicitWidth: showImage ? 16 : buttonText.implicitWidth
+ width: showImage ? 16 : buttonText.implicitWidth
- Layout.preferredHeight: fontMetrics.height
- font.family: Settings.emojiFont
-
- text: modelData
onClicked: {
room.input.reaction(row.model.eventId, modelData);
TimelineManager.focusMessageInput();
}
+
+ Label {
+ id: buttonText
+
+ anchors.centerIn: parent
+ color: button.hovered ? button.highlightColor : button.buttonTextColor
+ font.family: Settings.emojiFont
+ horizontalAlignment: Text.AlignHCenter
+ padding: 0
+ text: button.modelData
+ verticalAlignment: Text.AlignVCenter
+ visible: !button.showImage
+ }
+ Image {
+ id: buttonImg
+
+ // Workaround, can't get icon.source working for now...
+ anchors.fill: parent
+ fillMode: Image.PreserveAspectFit
+ source: button.showImage ? (button.modelData.replace("mxc://", "image://MxcImage/") + "?scale") : ""
+ sourceSize.height: button.height
+ sourceSize.width: button.width
+ }
+ NhekoCursorShape {
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ }
+ Ripple {
+ color: Qt.rgba(buttonTextColor.r, buttonTextColor.g, buttonTextColor.b, 0.5)
+ }
}
}
-
ImageButton {
- visible: !!row.model && row.model.isEditable
- buttonTextColor: Nheko.colors.buttonText
- width: 16
- hoverEnabled: true
- image: ":/icons/icons/ui/edit.svg"
- ToolTip.visible: hovered
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: qsTr("Edit")
+ ToolTip.visible: hovered
+ buttonTextColor: palette.buttonText
+ hoverEnabled: true
+ image: ":/icons/icons/ui/edit.svg"
+ visible: !!row.model && row.model.isEditable
+ width: 16
+
onClicked: {
- if (row.model.isEditable) room.edit = row.model.eventId;
+ if (row.model.isEditable)
+ room.edit = row.model.eventId;
}
}
-
ImageButton {
id: reactButton
- visible: room ? room.permissions.canSend(MtxEvent.Reaction) : false
- width: 16
- hoverEnabled: true
- image: ":/icons/icons/ui/smile-add.svg"
- ToolTip.visible: hovered
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: qsTr("React")
- onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(reactButton, room.roomId, function(plaintext, markdown) {
- var event_id = row.model ? row.model.eventId : "";
- room.input.reaction(event_id, plaintext);
- TimelineManager.focusMessageInput();
- })
- }
-
- ImageButton {
- visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
- width: 16
- hoverEnabled: true
- image: (row.model && row.model.threadId) ? ":/icons/icons/ui/thread.svg" : ":/icons/icons/ui/new-thread.svg"
ToolTip.visible: hovered
+ hoverEnabled: true
+ image: ":/icons/icons/ui/smile-add.svg"
+ visible: room ? room.permissions.canSend(MtxEvent.Reaction) : false
+ width: 16
+
+ onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(reactButton, room.roomId, function (plaintext, markdown) {
+ var event_id = row.model ? row.model.eventId : "";
+ room.input.reaction(event_id, plaintext);
+ TimelineManager.focusMessageInput();
+ })
+ }
+ ImageButton {
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: (row.model && row.model.threadId) ? qsTr("Reply in thread") : qsTr("New thread")
- onClicked: room.thread = (row.model.threadId || row.model.eventId)
- }
-
- ImageButton {
+ ToolTip.visible: hovered
+ hoverEnabled: true
+ image: (row.model && row.model.threadId) ? ":/icons/icons/ui/thread.svg" : ":/icons/icons/ui/new-thread.svg"
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
width: 16
- hoverEnabled: true
- image: ":/icons/icons/ui/reply.svg"
- ToolTip.visible: hovered
+
+ onClicked: room.thread = (row.model.threadId || row.model.eventId)
+ }
+ ImageButton {
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: qsTr("Reply")
+ ToolTip.visible: hovered
+ hoverEnabled: true
+ image: ":/icons/icons/ui/reply.svg"
+ visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
+ width: 16
+
onClicked: room.reply = row.model.eventId
}
-
ImageButton {
- visible: !!row.model && filteredTimeline.filterByContent
- buttonTextColor: Nheko.colors.buttonText
- width: 16
- hoverEnabled: true
- image: ":/icons/icons/ui/go-to.svg"
- ToolTip.visible: hovered
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: qsTr("Go to message")
+ ToolTip.visible: hovered
+ buttonTextColor: palette.buttonText
+ hoverEnabled: true
+ image: ":/icons/icons/ui/go-to.svg"
+ visible: !!row.model && filteredTimeline.filterByContent
+ width: 16
+
onClicked: {
topBar.searchString = "";
room.showEvent(row.model.eventId);
}
}
-
ImageButton {
id: optionsButton
- width: 16
- hoverEnabled: true
- image: ":/icons/icons/ui/options.svg"
- ToolTip.visible: hovered
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: qsTr("Options")
+ ToolTip.visible: hovered
+ hoverEnabled: true
+ image: ":/icons/icons/ui/options.svg"
+ width: 16
+
onClicked: messageContextMenu.show(row.model.eventId, row.model.threadId, row.model.type, row.model.isSender, row.model.isEncrypted, row.model.isEditable, "", row.model.body, optionsButton)
}
-
}
-
}
-
- ScrollHelper {
- flickable: parent
- anchors.fill: parent
- }
-
Shortcut {
sequence: StandardKey.MoveToPreviousPage
+
onActivated: {
chat.contentY = chat.contentY - chat.height * 0.9;
chat.returnToBounds();
}
}
-
Shortcut {
sequence: StandardKey.MoveToNextPage
+
onActivated: {
chat.contentY = chat.contentY + chat.height * 0.9;
chat.returnToBounds();
}
}
-
Shortcut {
sequence: StandardKey.Cancel
+
onActivated: {
- if(room.input.uploads.length > 0)
+ if (room.input.uploads.length > 0)
room.input.declineUploads();
- else if(room.reply)
+ else if (room.reply)
room.reply = undefined;
else if (room.edit)
room.edit = undefined;
else
- room.thread = undefined
+ room.thread = undefined;
TimelineManager.focusMessageInput();
}
}
@@ -247,19 +460,20 @@ Item {
// Better solution welcome.
Shortcut {
sequence: "Alt+Up"
+
onActivated: room.reply = room.indexToId(room.reply ? room.idToIndex(room.reply) + 1 : 0)
}
-
Shortcut {
sequence: "Alt+Down"
+
onActivated: {
var idx = room.reply ? room.idToIndex(room.reply) - 1 : -1;
room.reply = idx >= 0 ? room.indexToId(idx) : null;
}
}
-
Shortcut {
sequence: "Alt+F"
+
onActivated: {
if (room.reply) {
var forwardMess = forwardCompleterComponent.createObject(timelineRoot);
@@ -270,355 +484,162 @@ Item {
}
}
}
-
Shortcut {
sequence: "Ctrl+E"
+
onActivated: {
room.edit = room.reply;
}
}
-
- Window.onActiveChanged: readTimer.running = Window.active
-
Timer {
id: readTimer
+ interval: 1000
+
// force current read index to update
onTriggered: {
if (room)
- room.setCurrentIndex(room.currentIndex);
-
+ room.setCurrentIndex(room.currentIndex);
}
- interval: 1000
}
-
Component {
id: sectionHeader
Column {
- topPadding: userName_.visible? 4: 0
- bottomPadding: Settings.bubbles? (isSender && previousMessageDay == day? 0 : 2) : 3
+ bottomPadding: Settings.bubbles ? (isSender && previousMessageDay == day ? 0 : 2) : 3
spacing: 8
+ topPadding: userName_.visible ? 4 : 0
visible: (previousMessageUserId !== userId || previousMessageDay !== day || isStateEvent !== previousMessageIsStateEvent)
width: parentWidth
- height: ((previousMessageDay !== day) ? dateBubble.height : 0) + (isStateEvent? 0 : userName.height +8 )
Label {
id: dateBubble
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
- visible: room && previousMessageDay !== day
- text: room ? room.formatDateSeparator(timestamp) : ""
- color: Nheko.colors.text
+ color: palette.text
height: Math.round(fontMetrics.height * 1.4)
- width: contentWidth * 1.2
horizontalAlignment: Text.AlignHCenter
+ text: room ? room.formatDateSeparator(timestamp) : ""
verticalAlignment: Text.AlignVCenter
+ visible: room && previousMessageDay !== day
+ width: contentWidth * 1.2
background: Rectangle {
+ color: palette.window
radius: parent.height / 2
- color: Nheko.colors.window
}
-
}
-
Row {
+ id: userInfo
+
+ property int remainingWidth: chat.delegateMaxWidth - spacing - messageUserAvatar.width
+
height: userName_.height
spacing: 8
visible: !isStateEvent && (!isSender || !Settings.bubbles)
- id: userInfo
Avatar {
id: messageUserAvatar
- width: Nheko.avatarSize * (Settings.smallAvatars? 0.5 : 1)
- height: Nheko.avatarSize * (Settings.smallAvatars? 0.5 : 1)
- url: !room ? "" : room.avatarUrl(userId).replace("mxc://", "image://MxcImage/")
- displayName: userName
- userid: userId
- onClicked: room.openUserProfile(userId)
- ToolTip.visible: messageUserAvatar.hovered
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: userid
- }
+ ToolTip.visible: messageUserAvatar.hovered
+ displayName: userName
+ height: Nheko.avatarSize * (Settings.smallAvatars ? 0.5 : 1)
+ url: !room ? "" : room.avatarUrl(userId).replace("mxc://", "image://MxcImage/")
+ userid: userId
+ width: Nheko.avatarSize * (Settings.smallAvatars ? 0.5 : 1)
+ onClicked: room.openUserProfile(userId)
+ }
Connections {
function onRoomAvatarUrlChanged() {
messageUserAvatar.url = room.avatarUrl(userId).replace("mxc://", "image://MxcImage/");
}
-
function onScrollToIndex(index) {
chat.positionViewAtIndex(index, ListView.Center);
}
target: room
}
- property int remainingWidth: chat.delegateMaxWidth - spacing - messageUserAvatar.width
AbstractButton {
id: userNameButton
- contentItem: ElidedLabel {
- id: userName_
- fullText: userName
- color: TimelineManager.userColor(userId, Nheko.colors.base)
- textFormat: Text.RichText
- elideWidth: Math.min(userInfo.remainingWidth-Math.min(statusMsg.implicitWidth,userInfo.remainingWidth/3), userName_.fullTextWidth)
- }
- ToolTip.visible: hovered
+
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: userId
- onClicked: room.openUserProfile(userId)
+ ToolTip.visible: hovered
leftInset: 0
- rightInset: 0
leftPadding: 0
+ rightInset: 0
rightPadding: 0
- CursorShape {
+ contentItem: Label {
+ id: userName_
+
+ color: TimelineManager.userColor(userId, palette.base)
+ text: TimelineManager.escapeEmoji(userNameTextMetrics.elidedText)
+ textFormat: Text.RichText
+ }
+
+ onClicked: room.openUserProfile(userId)
+
+ TextMetrics {
+ id: userNameTextMetrics
+
+ elide: Text.ElideRight
+ elideWidth: userInfo.remainingWidth - Math.min(statusMsg.implicitWidth, userInfo.remainingWidth / 3)
+ text: userName
+ }
+ NhekoCursorShape {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
-
}
-
Label {
id: statusMsg
- anchors.baseline: userNameButton.baseline
- color: Nheko.colors.buttonText
- text: userStatus.replace(/\n/g, " ")
- textFormat: Text.PlainText
- elide: Text.ElideRight
- width: Math.min(implicitWidth, userInfo.remainingWidth - userName_.width - parent.spacing)
- font.italic: true
- font.pointSize: Math.floor(fontMetrics.font.pointSize * 0.8)
+
+ property string userStatus: Presence.userStatus(userId)
+
+ ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: qsTr("%1's status message").arg(userName)
ToolTip.visible: statusMsgHoverHandler.hovered
- ToolTip.delay: Nheko.tooltipDelay
+ anchors.baseline: userNameButton.baseline
+ color: palette.buttonText
+ elide: Text.ElideRight
+ font.italic: true
+ font.pointSize: Math.floor(fontMetrics.font.pointSize * 0.8)
+ text: userStatus.replace(/\n/g, " ")
+ textFormat: Text.PlainText
+ width: Math.min(implicitWidth, userInfo.remainingWidth - userName_.width - parent.spacing)
HoverHandler {
id: statusMsgHoverHandler
- }
- property string userStatus: Presence.userStatus(userId)
+ }
Connections {
- target: Presence
function onPresenceChanged(id) {
- if (id == userId) statusMsg.userStatus = Presence.userStatus(userId);
+ if (id == userId)
+ statusMsg.userStatus = Presence.userStatus(userId);
}
- }
- }
- }
-
- }
-
- }
-
- delegate: Item {
- id: wrapper
-
- required property double proportionalHeight
- required property int type
- required property string typeString
- required property int originalWidth
- required property string blurhash
- required property string body
- required property string formattedBody
- required property string eventId
- required property string filename
- required property string filesize
- required property string url
- required property string thumbnailUrl
- required property string duration
- required property bool isOnlyEmoji
- required property bool isSender
- required property bool isEncrypted
- required property bool isEditable
- required property bool isEdited
- required property bool isStateEvent
- property bool previousMessageIsStateEvent: (index + 1) >= chat.count ? true : chat.model.dataByIndex(index+1, Room.IsStateEvent)
- required property string replyTo
- required property string threadId
- required property string userId
- 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 var timestamp
- required property int status
- required property int index
- required property int relatedEventCacheBuster
- required property var day
- property string previousMessageUserId: (index + 1) >= chat.count ? "" : chat.model.dataByIndex(index+1, Room.UserId)
- property var previousMessageDay: (index + 1) >= chat.count ? 0 : chat.model.dataByIndex(index+1, Room.Day)
- required property string userName
- property bool scrolledToThis: eventId === room.scrollTarget && (y + height > chat.y + chat.contentY && y < chat.y + chat.height + chat.contentY)
-
- anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
- width: chat.delegateMaxWidth
- height: section.active ? section.height + timelinerow.height : timelinerow.height
- ListView.delayRemove: true
-
- Loader {
- id: section
-
- property int parentWidth: parent.width
- property string userId: wrapper.userId
- property string previousMessageUserId: wrapper.previousMessageUserId
- property var day: wrapper.day
- property var previousMessageDay: wrapper.previousMessageDay
- property bool previousMessageIsStateEvent: wrapper.previousMessageIsStateEvent
- property bool isStateEvent: wrapper.isStateEvent
- property bool isSender: wrapper.isSender
- property string userName: wrapper.userName
- property date timestamp: wrapper.timestamp
-
- z: 4
- active: previousMessageUserId !== userId || previousMessageDay !== day || previousMessageIsStateEvent !== isStateEvent
- //asynchronous: true
- sourceComponent: sectionHeader
- visible: status == Loader.Ready
- }
-
- TimelineRow {
- id: timelinerow
-
- proportionalHeight: wrapper.proportionalHeight
- type: chat.model, wrapper.type
- typeString: wrapper.typeString
- originalWidth: wrapper.originalWidth
- blurhash: wrapper.blurhash
- body: wrapper.body
- formattedBody: wrapper.formattedBody
- eventId: chat.model, wrapper.eventId
- filename: wrapper.filename
- filesize: wrapper.filesize
- url: wrapper.url
- thumbnailUrl: wrapper.thumbnailUrl
- duration: wrapper.duration
- isOnlyEmoji: wrapper.isOnlyEmoji
- isSender: wrapper.isSender
- isEncrypted: wrapper.isEncrypted
- isEditable: wrapper.isEditable
- isEdited: wrapper.isEdited
- isStateEvent: wrapper.isStateEvent
- replyTo: wrapper.replyTo
- threadId: wrapper.threadId
- userId: wrapper.userId
- userName: wrapper.userName
- roomTopic: wrapper.roomTopic
- roomName: wrapper.roomName
- callType: wrapper.callType
- reactions: wrapper.reactions
- trustlevel: wrapper.trustlevel
- notificationlevel: wrapper.notificationlevel
- encryptionError: wrapper.encryptionError
- timestamp: wrapper.timestamp
- status: wrapper.status
- index: wrapper.index
- relatedEventCacheBuster: wrapper.relatedEventCacheBuster
- y: section.visible && section.active ? section.y + section.height : 0
-
- onHoveredChanged: {
- if (!Settings.mobileMode && hovered) {
- if (!messageActions.hovered) {
- messageActions.attached = timelinerow;
- messageActions.model = timelinerow;
+ target: Presence
}
}
}
- background: Rectangle {
- id: scrollHighlight
-
- opacity: 0
- visible: true
- z: 1
- enabled: false
- color: Nheko.colors.highlight
-
- states: State {
- name: "revealed"
- when: wrapper.scrolledToThis
- }
-
- transitions: Transition {
- from: ""
- to: "revealed"
-
- SequentialAnimation {
- PropertyAnimation {
- target: scrollHighlight
- properties: "opacity"
- easing.type: Easing.InOutQuad
- from: 0
- to: 1
- duration: 500
- }
-
- PropertyAnimation {
- target: scrollHighlight
- properties: "opacity"
- easing.type: Easing.InOutQuad
- from: 1
- to: 0
- duration: 500
- }
-
- ScriptAction {
- script: room.eventShown()
- }
-
- }
-
- }
-
- }
}
-
- Connections {
- function onMovementEnded() {
- if (y + height + 2 * chat.spacing > chat.contentY + chat.height && y < chat.contentY + chat.height)
- chat.model.currentIndex = index;
-
- }
-
- target: chat
- }
-
- }
-
- footer: Item {
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.margins: Nheko.paddingLarge
- visible: (room && room.paginationInProgress) || chat.filteringInProgress
- // hacky, but works
- height: loadingSpinner.height + 2 * Nheko.paddingLarge
-
- Spinner {
- id: loadingSpinner
-
- anchors.centerIn: parent
- anchors.margins: Nheko.paddingLarge
- running: (room && room.paginationInProgress) || chat.filteringInProgress
- foreground: Nheko.colors.mid
- z: 3
- }
-
}
}
-
Platform.Menu {
id: messageContextMenu
property string eventId
- property string threadId
+ property int eventType
+ property bool isEditable
+ property bool isEncrypted
+ property bool isSender
property string link
property string text
- property int eventType
- property bool isEncrypted
- property bool isEditable
- property bool isSender
+ property string threadId
function show(eventId_, threadId_, eventType_, isSender_, isEncrypted_, isEditable_, link_, text_, showAt_) {
eventId = eventId_;
@@ -628,104 +649,106 @@ Item {
isEditable = isEditable_;
isSender = isSender_;
if (text_)
- text = text_;
+ text = text_;
else
- text = "";
+ text = "";
if (link_)
- link = link_;
+ link = link_;
else
- link = "";
+ link = "";
if (showAt_)
- open(showAt_);
+ open(showAt_);
else
- open();
+ open();
}
Component {
id: removeReason
+
InputDialog {
id: removeReasonDialog
property string eventId
- title: qsTr("Reason for removal")
prompt: qsTr("Enter reason for removal or hit enter for no reason:")
- onAccepted: function(text) {
+ title: qsTr("Reason for removal")
+
+ onAccepted: function (text) {
room.redactEvent(eventId, text);
}
}
}
-
Platform.MenuItem {
- visible: filteredTimeline.filterByContent
- enabled: visible
- text: qsTr("Go to &message")
- onTriggered: function() {
+ enabled: visible
+ text: qsTr("Go to &message")
+ visible: filteredTimeline.filterByContent
+
+ onTriggered: function () {
topBar.searchString = "";
room.showEvent(messageContextMenu.eventId);
}
- }
-
+ }
Platform.MenuItem {
- visible: messageContextMenu.text
enabled: visible
text: qsTr("&Copy")
+ visible: messageContextMenu.text
+
onTriggered: Clipboard.text = messageContextMenu.text
}
-
Platform.MenuItem {
- visible: messageContextMenu.link
enabled: visible
text: qsTr("Copy &link location")
+ visible: messageContextMenu.link
+
onTriggered: Clipboard.text = messageContextMenu.link
}
-
Platform.MenuItem {
id: reactionOption
- visible: room ? room.permissions.canSend(MtxEvent.Reaction) : false
text: qsTr("Re&act")
- onTriggered: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(null, room.roomId, function(plaintext, markdown) {
- room.input.reaction(messageContextMenu.eventId, plaintext);
- TimelineManager.focusMessageInput();
- })
- }
+ visible: room ? room.permissions.canSend(MtxEvent.Reaction) : false
+ onTriggered: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(null, room.roomId, function (plaintext, markdown) {
+ room.input.reaction(messageContextMenu.eventId, plaintext);
+ TimelineManager.focusMessageInput();
+ })
+ }
Platform.MenuItem {
- visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
text: qsTr("Repl&y")
+ visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
+
onTriggered: room.reply = (messageContextMenu.eventId)
}
-
Platform.MenuItem {
- visible: messageContextMenu.isEditable && (room ? room.permissions.canSend(MtxEvent.TextMessage) : false)
enabled: visible
text: qsTr("&Edit")
+ visible: messageContextMenu.isEditable && (room ? room.permissions.canSend(MtxEvent.TextMessage) : false)
+
onTriggered: room.edit = (messageContextMenu.eventId)
}
-
Platform.MenuItem {
- visible: (room ? room.permissions.canSend(MtxEvent.TextMessage) : false)
enabled: visible
text: qsTr("&Thread")
+ visible: (room ? room.permissions.canSend(MtxEvent.TextMessage) : false)
+
onTriggered: room.thread = (messageContextMenu.threadId || messageContextMenu.eventId)
}
-
Platform.MenuItem {
- visible: (room ? room.permissions.canChange(MtxEvent.PinnedEvents) : false)
enabled: visible
text: visible && room.pinnedMessages.includes(messageContextMenu.eventId) ? qsTr("Un&pin") : qsTr("&Pin")
+ visible: (room ? room.permissions.canChange(MtxEvent.PinnedEvents) : false)
+
onTriggered: visible && room.pinnedMessages.includes(messageContextMenu.eventId) ? room.unpin(messageContextMenu.eventId) : room.pin(messageContextMenu.eventId)
}
-
Platform.MenuItem {
text: qsTr("&Read receipts")
+
onTriggered: room.showReadReceipts(messageContextMenu.eventId)
}
-
Platform.MenuItem {
- visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker || messageContextMenu.eventType == MtxEvent.TextMessage || messageContextMenu.eventType == MtxEvent.LocationMessage || messageContextMenu.eventType == MtxEvent.EmoteMessage || messageContextMenu.eventType == MtxEvent.NoticeMessage
text: qsTr("&Forward")
+ visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker || messageContextMenu.eventType == MtxEvent.TextMessage || messageContextMenu.eventType == MtxEvent.LocationMessage || messageContextMenu.eventType == MtxEvent.EmoteMessage || messageContextMenu.eventType == MtxEvent.NoticeMessage
+
onTriggered: {
var forwardMess = forwardCompleterComponent.createObject(timelineRoot);
forwardMess.setMessageEventId(messageContextMenu.eventId);
@@ -733,28 +756,27 @@ Item {
timelineRoot.destroyOnClose(forwardMess);
}
}
-
Platform.MenuItem {
text: qsTr("&Mark as read")
}
-
Platform.MenuItem {
text: qsTr("View raw message")
+
onTriggered: room.viewRawMessage(messageContextMenu.eventId)
}
-
Platform.MenuItem {
- // TODO(Nico): Fix this still being iterated over, when using keyboard to select options
- visible: messageContextMenu.isEncrypted
enabled: visible
text: qsTr("View decrypted raw message")
+ // TODO(Nico): Fix this still being iterated over, when using keyboard to select options
+ visible: messageContextMenu.isEncrypted
+
onTriggered: room.viewDecryptedRawMessage(messageContextMenu.eventId)
}
-
Platform.MenuItem {
- visible: (room ? room.permissions.canRedact() : false) || messageContextMenu.isSender
text: qsTr("Remo&ve message")
- onTriggered: function() {
+ visible: (room ? room.permissions.canRedact() : false) || messageContextMenu.isSender
+
+ onTriggered: function () {
var dialog = removeReason.createObject(timelineRoot);
dialog.eventId = messageContextMenu.eventId;
dialog.show();
@@ -762,44 +784,40 @@ Item {
timelineRoot.destroyOnClose(dialog);
}
}
-
Platform.MenuItem {
- visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker
enabled: visible
text: qsTr("&Save as")
+ visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker
+
onTriggered: room.saveMedia(messageContextMenu.eventId)
}
-
Platform.MenuItem {
- visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker
enabled: visible
text: qsTr("&Open in external program")
+ visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker
+
onTriggered: room.openMedia(messageContextMenu.eventId)
}
-
Platform.MenuItem {
- visible: messageContextMenu.eventId
enabled: visible
text: qsTr("Copy link to eve&nt")
+ visible: messageContextMenu.eventId
+
onTriggered: room.copyLinkToEvent(messageContextMenu.eventId)
}
-
}
-
Component {
id: forwardCompleterComponent
ForwardCompleter {
}
-
}
-
Platform.Menu {
id: replyContextMenu
- property string text
- property string link
property string eventId
+ property string link
+ property string text
function show(text_, link_, eventId_) {
text = text_;
@@ -809,85 +827,100 @@ Item {
}
Platform.MenuItem {
- visible: replyContextMenu.text
enabled: visible
text: qsTr("&Copy")
+ visible: replyContextMenu.text
+
onTriggered: Clipboard.text = replyContextMenu.text
}
-
Platform.MenuItem {
- visible: replyContextMenu.link
enabled: visible
text: qsTr("Copy &link location")
+ visible: replyContextMenu.link
+
onTriggered: Clipboard.text = replyContextMenu.link
}
-
Platform.MenuItem {
- visible: true
enabled: visible
text: qsTr("&Go to quoted message")
+ visible: true
+
onTriggered: room.showEvent(replyContextMenu.eventId)
}
-
}
RoundButton {
id: toEndButton
- anchors {
- bottom: parent.bottom
- right: scrollbar.left
- bottomMargin: Nheko.paddingMedium+(fullWidth-width)/2
- rightMargin: Nheko.paddingMedium+(fullWidth-width)/2
- }
+
property int fullWidth: 40
- width: 0
- height: width
- radius: width/2
- onClicked: function() { chat.positionViewAtBeginning(); TimelineManager.focusMessageInput(); }
+
flat: true
+ height: width
hoverEnabled: true
+ radius: width / 2
+ width: 0
background: Rectangle {
- color: toEndButton.down ? Nheko.colors.highlight : Nheko.colors.button
- opacity: enabled ? 1 : 0.3
- border.color: toEndButton.hovered ? Nheko.colors.highlight : Nheko.colors.buttonText
+ border.color: toEndButton.hovered ? palette.highlight : palette.buttonText
border.width: 1
+ color: toEndButton.down ? palette.highlight : palette.button
+ opacity: enabled ? 1 : 0.3
radius: toEndButton.radius
}
-
states: [
State {
name: ""
- PropertyChanges { target: toEndButton; width: 0 }
+
+ PropertyChanges {
+ target: toEndButton
+ width: 0
+ }
},
State {
name: "shown"
when: !chat.atYEnd
- PropertyChanges { target: toEndButton; width: toEndButton.fullWidth }
+
+ PropertyChanges {
+ target: toEndButton
+ width: toEndButton.fullWidth
+ }
}
]
-
- Image {
- id: buttonImg
- anchors.fill: parent
- anchors.margins: Nheko.paddingMedium
- source: "image://colorimage/:/icons/icons/ui/download.svg?" + (toEndButton.down ? Nheko.colors.highlightedText : Nheko.colors.buttonText)
- fillMode: Image.PreserveAspectFit
- }
-
transitions: Transition {
from: ""
- to: "shown"
reversible: true
+ to: "shown"
SequentialAnimation {
- PauseAnimation { duration: 500 }
+ PauseAnimation {
+ duration: 500
+ }
PropertyAnimation {
- target: toEndButton
- properties: "width"
- easing.type: Easing.InOutQuad
duration: 200
+ easing.type: Easing.InOutQuad
+ properties: "width"
+ target: toEndButton
}
}
}
+
+ onClicked: function () {
+ chat.positionViewAtBeginning();
+ TimelineManager.focusMessageInput();
+ }
+
+ anchors {
+ bottom: parent.bottom
+ bottomMargin: Nheko.paddingMedium + (fullWidth - width) / 2
+ right: scrollbar.left
+ rightMargin: Nheko.paddingMedium + (fullWidth - width) / 2
+ }
+ Image {
+ id: buttonImg
+
+ anchors.fill: parent
+ anchors.margins: Nheko.paddingMedium
+ fillMode: Image.PreserveAspectFit
+ source: "image://colorimage/:/icons/icons/ui/download.svg?" + (toEndButton.down ? palette.highlightedText : palette.buttonText)
+ }
}
}
diff --git a/resources/qml/PrivacyScreen.qml b/resources/qml/PrivacyScreen.qml
index 5967f25d..a3539df7 100644
--- a/resources/qml/PrivacyScreen.qml
+++ b/resources/qml/PrivacyScreen.qml
@@ -2,18 +2,17 @@
//
// SPDX-License-Identifier: GPL-3.0-or-later
-import QtGraphicalEffects 1.0
-import QtQuick 2.12
-import QtQuick.Window 2.2
-import im.nheko 1.0
+import QtQuick
+import QtQuick.Window
+import im.nheko
+import QtQuick.Effects
Item {
id: privacyScreen
readonly property bool active: Settings.privacyScreen && screenSaver.state === "Visible"
- property var timelineRoot
property int screenTimeout
-
+ property var timelineRoot
required property var windowTarget
Connections {
@@ -24,29 +23,28 @@ Item {
} else {
if (timelineRoot.visible)
screenSaverTimer.start();
-
}
}
target: windowTarget
}
-
Timer {
id: screenSaverTimer
interval: screenTimeout * 1000
running: !windowTarget.active
+
onTriggered: {
screenSaver.state = "Visible";
}
}
-
Item {
id: screenSaver
- state: "Invisible"
anchors.fill: parent
+ state: "Invisible"
visible: false
+
states: [
State {
name: "Visible"
@@ -55,20 +53,18 @@ Item {
target: screenSaver
visible: true
}
-
PropertyChanges {
- target: screenSaver
opacity: 1
+ target: screenSaver
}
},
State {
name: "Invisible"
PropertyChanges {
- target: screenSaver
opacity: 0
+ target: screenSaver
}
-
PropertyChanges {
target: screenSaver
visible: false
@@ -78,36 +74,33 @@ Item {
transitions: [
Transition {
from: "Invisible"
- to: "Visible"
reversible: true
+ to: "Visible"
SequentialAnimation {
NumberAnimation {
- target: screenSaver
- property: "visible"
duration: 0
- }
-
- NumberAnimation {
+ property: "visible"
target: screenSaver
- property: "opacity"
+ }
+ NumberAnimation {
duration: 300
easing.type: Easing.Linear
+ property: "opacity"
+ target: screenSaver
}
-
}
-
}
]
- FastBlur {
+ MultiEffect {
id: blur
anchors.fill: parent
+ blur: 1.0
+ blurEnabled: true
+ blurMax: 32
source: timelineRoot
- radius: 50
}
-
}
-
}
diff --git a/resources/qml/QuickSwitcher.qml b/resources/qml/QuickSwitcher.qml
index 916a4cee..67718ecb 100644
--- a/resources/qml/QuickSwitcher.qml
+++ b/resources/qml/QuickSwitcher.qml
@@ -11,84 +11,83 @@ Popup {
id: quickSwitcher
property int textHeight: Math.round(Qt.application.font.pixelSize * 2.4)
+ property int textMargin: Nheko.paddingSmall
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)
y: Math.round(parent.height / 4)
- modal: true
- closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
- parent: Overlay.overlay
- palette: Nheko.colors
+
+ Overlay.modal: Rectangle {
+ color: "#aa1E1E1E"
+ }
+
+ onClosed: TimelineManager.focusMessageInput()
onOpened: {
roomTextInput.forceActiveFocus();
}
- onClosed: TimelineManager.focusMessageInput()
- property int textMargin: Nheko.paddingSmall
- Column{
+ Column {
anchors.fill: parent
spacing: 1
MatrixTextField {
id: roomTextInput
- width: parent.width
+ color: palette.text
font.pixelSize: Math.ceil(quickSwitcher.textHeight * 0.6)
- color: Nheko.colors.text
- onTextEdited: {
- completerPopup.completer.searchString = text;
- }
- Keys.onPressed: {
+ width: parent.width
+
+ Keys.onPressed: event => {
if (event.key == Qt.Key_Up || event.key == Qt.Key_Backtab) {
event.accepted = true;
completerPopup.up();
} else if (event.key == Qt.Key_Down || event.key == Qt.Key_Tab) {
event.accepted = true;
if (event.key == Qt.Key_Tab && (event.modifiers & Qt.ShiftModifier))
- completerPopup.up();
+ completerPopup.up();
else
- completerPopup.down();
+ completerPopup.down();
} else if (event.matches(StandardKey.InsertParagraphSeparator)) {
completerPopup.finishCompletion();
event.accepted = true;
}
}
+ onTextEdited: {
+ completerPopup.completer.searchString = text;
+ }
}
-
Completer {
id: completerPopup
- visible: roomTextInput.text.length > 0
- width: parent.width
- completerName: "room"
- bottomToTop: false
- fullWidth: true
avatarHeight: quickSwitcher.textHeight
avatarWidth: quickSwitcher.textHeight
+ bottomToTop: false
centerRowContent: false
+ completerName: "room"
+ fullWidth: true
rowMargin: Math.round(quickSwitcher.textMargin / 2)
rowSpacing: quickSwitcher.textMargin
+ visible: roomTextInput.text.length > 0
+ width: parent.width
}
}
-
Connections {
function onCompletionSelected(id) {
Rooms.setCurrentRoom(id);
quickSwitcher.close();
}
-
function onCountChanged() {
if (completerPopup.count > 0 && (completerPopup.currentIndex < 0 || completerPopup.currentIndex >= completerPopup.count))
- completerPopup.currentIndex = 0;
-
+ completerPopup.currentIndex = 0;
}
target: completerPopup
}
-
- Overlay.modal: Rectangle {
- color: "#aa1E1E1E"
- }
-
}
diff --git a/resources/qml/Reactions.qml b/resources/qml/Reactions.qml
index cce8720a..5ab58beb 100644
--- a/resources/qml/Reactions.qml
+++ b/resources/qml/Reactions.qml
@@ -11,10 +11,11 @@ import im.nheko 1.0
Flow {
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
+
+ // 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
spacing: 4
@@ -25,40 +26,39 @@ Flow {
delegate: AbstractButton {
id: reaction
- hoverEnabled: true
- ToolTip.visible: hovered
ToolTip.delay: Nheko.tooltipDelay
- onClicked: {
- console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + ". selfReactedEvent: " + modelData.selfReactedEvent);
- room.input.reaction(reactionFlow.eventId, modelData.key);
- }
- 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
+ ToolTip.visible: hovered
+ hoverEnabled: true
+ 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 {
spacing: textMetrics.height / 4
TextMetrics {
id: textMetrics
- font.family: Settings.emojiFont
elide: Text.ElideRight
elideWidth: 150
+ font.family: Settings.emojiFont
text: modelData.displayKey
}
-
Text {
id: reactionText
anchors.baseline: reactionCounter.baseline
+ color: (reaction.hovered || modelData.selfReactedEvent !== '') ? palette.highlightedText : palette.text
+ font.family: Settings.emojiFont
+ maximumLineCount: 1
text: {
// 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) {
@@ -68,51 +68,45 @@ Flow {
}
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://")
}
Image {
anchors.verticalCenter: divider.verticalCenter
+ fillMode: Image.PreserveAspectFit
height: textMetrics.height
- width: textMetrics.height
source: modelData.key.startsWith("mxc://") ? (modelData.key.replace("mxc://", "image://MxcImage/") + "?scale") : ""
visible: modelData.key.startsWith("mxc://")
- fillMode: Image.PreserveAspectFit
+ width: textMetrics.height
}
-
Rectangle {
id: divider
+ color: reaction.hovered ? palette.text : gentleText
height: Math.floor(reactionCounter.implicitHeight * 1.4)
width: 1
- color: reaction.hovered ? Nheko.colors.text: gentleText
}
-
Text {
id: reactionCounter
anchors.verticalCenter: divider.verticalCenter
- text: modelData.count
+ color: (reaction.hovered || modelData.selfReactedEvent !== '') ? palette.highlightedText : palette.windowText
font: reaction.font
- color: (reaction.hovered || modelData.selfReactedEvent !== '') ? Nheko.colors.highlightedText: Nheko.colors.windowText
+ text: modelData.count
}
-
}
- background: Rectangle {
- anchors.centerIn: parent
- implicitWidth: reaction.implicitWidth
- implicitHeight: reaction.implicitHeight
- border.color: reaction.hovered ? Nheko.colors.text: gentleText
- color: reaction.hovered ? Nheko.colors.highlight : (modelData.selfReactedEvent !== '' ? gentleHighlight : Nheko.colors.window)
- border.width: 1
- radius: reaction.height / 2
+ Component.onCompleted: {
+ ToolTip.text = Qt.binding(function () {
+ if (textMetrics.elidedText === textMetrics.text) {
+ return modelData.users;
+ }
+ return modelData.displayKey + "\n" + modelData.users;
+ });
+ }
+ onClicked: {
+ console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + ". selfReactedEvent: " + modelData.selfReactedEvent);
+ room.input.reaction(reactionFlow.eventId, modelData.key);
}
-
}
-
}
-
}
diff --git a/resources/qml/ReplyPopup.qml b/resources/qml/ReplyPopup.qml
index 365c5bff..ce24297c 100644
--- a/resources/qml/ReplyPopup.qml
+++ b/resources/qml/ReplyPopup.qml
@@ -12,91 +12,89 @@ Rectangle {
id: replyPopup
Layout.fillWidth: true
- visible: room && (room.reply || room.edit || room.thread)
+ color: palette.window
// Height of child, plus margins, plus border
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
Reply {
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.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.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.topMargin: Nheko.paddingSmall
- userColor: TimelineManager.userColor(modelData.userId, Nheko.colors.window)
blurhash: modelData.blurhash ?? ""
body: modelData.body ?? ""
- formattedBody: modelData.formattedBody ?? ""
+ encryptionError: modelData.encryptionError ?? 0
eventId: modelData.eventId ?? ""
filename: modelData.filename ?? ""
filesize: modelData.filesize ?? ""
+ formattedBody: modelData.formattedBody ?? ""
+ isOnlyEmoji: modelData.isOnlyEmoji ?? false
+ originalWidth: modelData.originalWidth ?? 0
proportionalHeight: modelData.proportionalHeight ?? 1
type: modelData.type ?? MtxEvent.UnknownMessage
typeString: modelData.typeString ?? ""
url: modelData.url ?? ""
- originalWidth: modelData.originalWidth ?? 0
- isOnlyEmoji: modelData.isOnlyEmoji ?? false
+ userColor: TimelineManager.userColor(modelData.userId, palette.window)
userId: modelData.userId ?? ""
userName: modelData.userName ?? ""
- encryptionError: modelData.encryptionError ?? ""
+ visible: room && room.reply
width: parent.width
}
-
ImageButton {
id: closeReplyButton
- visible: room && room.reply
+ ToolTip.text: qsTr("Close")
+ ToolTip.visible: closeReplyButton.hovered
+ anchors.margins: Nheko.paddingSmall
anchors.right: replyPreview.right
anchors.top: replyPreview.top
- anchors.margins: Nheko.paddingSmall
- hoverEnabled: true
- width: 16
height: 16
+ hoverEnabled: true
image: ":/icons/icons/ui/dismiss.svg"
- ToolTip.visible: closeReplyButton.hovered
- ToolTip.text: qsTr("Close")
+ visible: room && room.reply
+ width: 16
+
onClicked: room.reply = undefined
}
-
ImageButton {
id: closeEditButton
- visible: room && room.edit
- anchors.right: closeThreadButton.left
+ ToolTip.text: qsTr("Cancel Edit")
+ ToolTip.visible: closeEditButton.hovered
anchors.margins: 8
+ anchors.right: closeThreadButton.left
anchors.top: parent.top
+ height: 22
hoverEnabled: true
image: ":/icons/icons/ui/dismiss_edit.svg"
+ visible: room && room.edit
width: 22
- height: 22
- ToolTip.visible: closeEditButton.hovered
- ToolTip.text: qsTr("Cancel Edit")
+
onClicked: room.edit = undefined
}
-
ImageButton {
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.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
}
-
}
diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml
index 49a36d8f..92e7ef6d 100644
--- a/resources/qml/RoomList.qml
+++ b/resources/qml/RoomList.qml
@@ -18,454 +18,142 @@ Page {
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
property bool collapsed: false
- // HACK: https://bugreports.qt.io/browse/QTBUG-83972, qtwayland cannot auto hide menu
- Connections {
- function onHideMenu() {
- userInfoMenu.close()
- roomContextMenu.close()
- }
- target: MainWindow
- }
-
- Component {
- id: roomDirectoryComponent
-
- RoomDirectory {
- }
-
- }
-
- Component {
- id: createRoomComponent
-
- CreateRoom {
- }
- }
-
- Component {
- id: createDirectComponent
-
- CreateDirect {
- }
- }
-
- ListView {
- id: roomlist
-
- anchors.left: parent.left
- anchors.right: parent.right
- height: parent.height
- model: Rooms
- //reuseItems: true
-
- ScrollBar.vertical: ScrollBar {
- id: scrollbar
- parent: !collapsed && Settings.scrollbarsInRoomlist ? roomlist : null
- }
-
- ScrollHelper {
- flickable: parent
- anchors.fill: parent
- }
-
- Connections {
- function onCurrentRoomChanged() {
- if (Rooms.currentRoom)
- roomlist.positionViewAtIndex(Rooms.roomidToIndex(Rooms.currentRoom.roomId), ListView.Contain);
-
- }
-
- target: Rooms
- }
-
- Component {
- id: roomWindowComponent
-
- ApplicationWindow {
- id: roomWindowW
-
- property var room: null
- property var roomPreview: null
-
- Component.onCompleted: {
- MainWindow.addPerRoomWindow(room.roomId || roomPreview.roomid, roomWindowW);
- Nheko.setTransientParent(roomWindowW, null);
- }
- Component.onDestruction: MainWindow.removePerRoomWindow(room.roomId || roomPreview.roomid, roomWindowW)
-
- height: 650
- width: 420
- minimumWidth: 150
- minimumHeight: 150
- palette: Nheko.colors
- color: Nheko.colors.window
- title: room.plainRoomName
- //flags: Qt.Window | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
-
- Shortcut {
- sequence: StandardKey.Cancel
- onActivated: roomWindowW.close()
- }
-
- TimelineView {
- id: timeline
-
- privacyScreen: privacyScreen
- anchors.fill: parent
- room: roomWindowW.room
- roomPreview: roomWindowW.roomPreview.roomid ? roomWindowW.roomPreview : null
- }
-
- PrivacyScreen {
- id: privacyScreen
-
- anchors.fill: parent
- visible: Settings.privacyScreen
- screenTimeout: Settings.privacyScreenTimeout
- timelineRoot: timeline
- windowTarget: roomWindowW
- }
-
- onActiveChanged: { room.lastReadIdOnWindowFocus(); }
- }
-
- }
-
-
- Component {
- id: nestedSpaceMenuLevel
-
- SpaceMenuLevel {
- roomid: roomContextMenu.roomid
- childMenu: rootSpaceMenu.childMenu
- }
- }
-
-
- Platform.Menu {
- id: roomContextMenu
-
- property string roomid
- property var tags
-
- function show(roomid_, tags_) {
- roomid = roomid_;
- tags = tags_;
- open();
- }
-
- InputDialog {
- id: newTag
-
- title: qsTr("New tag")
- prompt: qsTr("Enter the tag you want to use:")
- onAccepted: function(text) {
- Rooms.toggleTag(roomContextMenu.roomid, "u." + text, true);
- }
- }
-
- Platform.MenuItem {
- text: qsTr("Open separately")
- onTriggered: {
- var roomWindow = roomWindowComponent.createObject(null, {
- "room": Rooms.getRoomById(roomContextMenu.roomid),
- "roomPreview": Rooms.getRoomPreviewById(roomContextMenu.roomid)
- });
- roomWindow.showNormal();
- destroyOnClose(roomWindow);
- }
- }
-
- Platform.MenuItem {
- text: qsTr("Room settings")
- onTriggered: TimelineManager.openRoomSettings(roomContextMenu.roomid)
- }
-
- Platform.MenuItem {
- text: qsTr("Leave room")
- onTriggered: TimelineManager.openLeaveRoomDialog(roomContextMenu.roomid)
- }
-
- Platform.MenuItem {
- text: qsTr("Copy room link")
- onTriggered: Rooms.copyLink(roomContextMenu.roomid)
- }
-
- Platform.Menu {
- id: tagsMenu
- title: qsTr("Tag room as:")
-
- Instantiator {
- model: Communities.tagsWithDefault
- onObjectAdded: tagsMenu.insertItem(index, object)
- onObjectRemoved: tagsMenu.removeItem(object)
-
- delegate: Platform.MenuItem {
- property string t: modelData
-
- text: {
- switch (t) {
- case "m.favourite":
- return qsTr("Favourite");
- case "m.lowpriority":
- return qsTr("Low priority");
- case "m.server_notice":
- return qsTr("Server notice");
- default:
- return t.substring(2);
- }
- }
- checkable: true
- checked: roomContextMenu.tags !== undefined && roomContextMenu.tags.includes(t)
- onTriggered: Rooms.toggleTag(roomContextMenu.roomid, t, checked)
- }
-
- }
-
- Platform.MenuItem {
- text: qsTr("Create new tag...")
- onTriggered: newTag.show()
- }
- }
-
- SpaceMenuLevel {
- id: rootSpaceMenu
-
- roomid: roomContextMenu.roomid
- position: -1
- title: qsTr("Add or remove from community...")
- childMenu: nestedSpaceMenuLevel
- }
- }
-
- delegate: ItemDelegate {
- id: roomItem
-
- 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 string roomName
- required property string roomId
- required property string avatarUrl
- required property string time
- required property string lastMessage
- required property var tags
- required property bool isInvite
- required property bool isSpace
- required property int notificationCount
- required property bool hasLoudNotification
- required property bool hasUnreadMessages
- required property bool isDirect
- required property string directChatOtherUserId
-
- Ripple {
- color: Qt.rgba(Nheko.colors.dark.r, Nheko.colors.dark.g, Nheko.colors.dark.b, 0.5)
- }
-
- 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.delay: Nheko.tooltipDelay
- ToolTip.text: roomName
- onClicked: {
- console.log("tapped " + roomId);
-
- if (!Rooms.currentRoom || Rooms.currentRoom.roomId !== roomId)
- Rooms.setCurrentRoom(roomId);
- else
- Rooms.resetCurrentRoom();
- }
- onPressAndHold: {
- if (!isInvite)
- roomContextMenu.show(roomId, tags);
-
- }
- states: [
- State {
- name: "highlight"
- when: roomItem.hovered && !((Rooms.currentRoom && roomId == Rooms.currentRoom.roomId) || Rooms.currentRoomPreview.roomid == roomId)
-
- PropertyChanges {
- target: roomItem
- backgroundColor: Nheko.colors.dark
- importantText: Nheko.colors.brightText
- unimportantText: Nheko.colors.brightText
- bubbleBackground: Nheko.colors.highlight
- bubbleText: Nheko.colors.highlightedText
- }
-
- },
- State {
- name: "selected"
- when: (Rooms.currentRoom && roomId == Rooms.currentRoom.roomId) || Rooms.currentRoomPreview.roomid == roomId
-
- PropertyChanges {
- target: roomItem
- backgroundColor: Nheko.colors.highlight
- importantText: Nheko.colors.highlightedText
- unimportantText: Nheko.colors.highlightedText
- bubbleBackground: Nheko.colors.highlightedText
- bubbleText: Nheko.colors.highlight
- }
-
- }
- ]
-
- // NOTE(Nico): We want to prevent the touch areas from overlapping. For some reason we need to add 1px of padding for that...
- Item {
- anchors.fill: parent
- anchors.margins: 1
-
- TapHandler {
- acceptedButtons: Qt.RightButton
- onSingleTapped: {
- if (!TimelineManager.isInvite)
- roomContextMenu.show(roomId, tags);
-
- }
- gesturePolicy: TapHandler.ReleaseWithinBounds
- acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
- }
-
- }
-
- RowLayout {
- spacing: Nheko.paddingMedium
- anchors.fill: parent
- anchors.margins: Nheko.paddingMedium
-
- Avatar {
- id: avatar
-
- enabled: false
- Layout.alignment: Qt.AlignVCenter
- height: avatarSize
- width: avatarSize
- url: avatarUrl.replace("mxc://", "image://MxcImage/")
- displayName: roomName
- userid: isDirect ? directChatOtherUserId : ""
- roomid: roomId
-
- NotificationBubble {
- id: collapsedNotificationBubble
-
- notificationCount: roomItem.notificationCount
- hasLoudNotification: roomItem.hasLoudNotification
- bubbleBackgroundColor: roomItem.bubbleBackground
- bubbleTextColor: roomItem.bubbleText
- anchors.right: parent.right
- anchors.bottom: parent.bottom
- anchors.margins: -Nheko.paddingSmall
- mayBeVisible: collapsed && (isSpace ? Settings.spaceNotifications : true)
- }
-
- }
-
- ColumnLayout {
- id: textContent
-
- visible: !collapsed
- Layout.alignment: Qt.AlignLeft
- Layout.fillWidth: true
- Layout.minimumWidth: 100
- width: parent.width - avatar.width
- Layout.preferredWidth: parent.width - avatar.width
- height: avatar.height
- spacing: Nheko.paddingSmall
-
- NotificationBubble {
- id: notificationBubble
-
- parent: isSpace ? titleRow : subtextRow
- notificationCount: roomItem.notificationCount
- hasLoudNotification: roomItem.hasLoudNotification
- bubbleBackgroundColor: roomItem.bubbleBackground
- bubbleTextColor: roomItem.bubbleText
- Layout.alignment: Qt.AlignRight
- Layout.leftMargin: Nheko.paddingSmall
- Layout.preferredWidth: implicitWidth
- Layout.preferredHeight: implicitHeight
- mayBeVisible: !collapsed && (isSpace ? Settings.spaceNotifications : true)
- }
-
- RowLayout {
- id: titleRow
-
- Layout.alignment: Qt.AlignTop
- Layout.fillWidth: true
- spacing: Nheko.paddingSmall
-
- ElidedLabel {
- id: rN
- Layout.alignment: Qt.AlignBaseline
- color: roomItem.importantText
- elideWidth: width
- fullText: TimelineManager.htmlEscape(roomName)
- textFormat: Text.RichText
- Layout.fillWidth: true
- }
-
- Label {
- id: timestamp
-
- visible: !isInvite && !isSpace
- width: visible ? 0 : undefined
- Layout.alignment: Qt.AlignRight | Qt.AlignBaseline
- font.pixelSize: fontMetrics.font.pixelSize * 0.9
- color: roomItem.unimportantText
- text: time
- }
-
- }
-
- RowLayout {
- id: subtextRow
-
- Layout.fillWidth: true
- spacing: 0
- visible: !isSpace
- height: visible ? 0 : undefined
- Layout.alignment: Qt.AlignBottom
-
- ElidedLabel {
- color: roomItem.unimportantText
- font.pixelSize: fontMetrics.font.pixelSize * 0.9
- elideWidth: width
- fullText: TimelineManager.htmlEscape(lastMessage)
- textFormat: Text.RichText
- Layout.fillWidth: true
- }
-
- }
-
- }
-
- }
-
- Rectangle {
- anchors.left: parent.left
- anchors.verticalCenter: parent.verticalCenter
- height: parent.height - Nheko.paddingSmall * 2
- width: 3
- color: Nheko.colors.highlight
- visible: hasUnreadMessages
- }
-
- background: Rectangle {
- color: backgroundColor
- }
-
- }
-
- }
-
background: Rectangle {
color: Nheko.theme.sidebarBackground
}
+ footer: ColumnLayout {
+ spacing: 0
+ Rectangle {
+ Layout.fillWidth: true
+ color: Nheko.theme.separator
+ height: 1
+ }
+ Pane {
+ Layout.alignment: Qt.AlignBottom
+ Layout.fillWidth: true
+ Layout.minimumHeight: 40
+ horizontalPadding: Nheko.paddingMedium
+ verticalPadding: 0
+
+ background: Rectangle {
+ color: palette.window
+ }
+ contentItem: RowLayout {
+ id: buttonRow
+
+ ImageButton {
+ Layout.fillWidth: true
+ Layout.margins: Nheko.paddingMedium
+ ToolTip.delay: Nheko.tooltipDelay
+ ToolTip.text: qsTr("Start a new chat")
+ ToolTip.visible: hovered
+ height: 22
+ hoverEnabled: true
+ image: ":/icons/icons/ui/add-square-button.svg"
+ width: 22
+
+ onClicked: roomJoinCreateMenu.open(parent)
+
+ Platform.Menu {
+ id: roomJoinCreateMenu
+
+ Platform.MenuItem {
+ text: qsTr("Join a room")
+
+ onTriggered: Nheko.openJoinRoomDialog()
+ }
+ Platform.MenuItem {
+ text: qsTr("Create a new room")
+
+ onTriggered: {
+ var createRoom = createRoomComponent.createObject(timelineRoot);
+ createRoom.show();
+ timelineRoot.destroyOnClose(createRoom);
+ }
+ }
+ Platform.MenuItem {
+ text: qsTr("Start a direct chat")
+
+ onTriggered: {
+ var createDirect = createDirectComponent.createObject(timelineRoot);
+ createDirect.show();
+ timelineRoot.destroyOnClose(createDirect);
+ }
+ }
+ Platform.MenuItem {
+ text: qsTr("Create a new community")
+
+ onTriggered: {
+ var createRoom = createRoomComponent.createObject(timelineRoot, {
+ "space": true
+ });
+ createRoom.show();
+ timelineRoot.destroyOnClose(createRoom);
+ }
+ }
+ }
+ }
+ ImageButton {
+ Layout.fillWidth: true
+ Layout.margins: Nheko.paddingMedium
+ ToolTip.delay: Nheko.tooltipDelay
+ ToolTip.text: qsTr("Room directory")
+ ToolTip.visible: hovered
+ height: 22
+ hoverEnabled: true
+ image: ":/icons/icons/ui/room-directory.svg"
+ visible: !collapsed
+ width: 22
+
+ onClicked: {
+ var win = roomDirectoryComponent.createObject(timelineRoot);
+ win.show();
+ timelineRoot.destroyOnClose(win);
+ }
+ }
+ ImageButton {
+ Layout.fillWidth: true
+ Layout.margins: Nheko.paddingMedium
+ ToolTip.delay: Nheko.tooltipDelay
+ ToolTip.text: qsTr("Search rooms (Ctrl+K)")
+ ToolTip.visible: hovered
+ height: 22
+ hoverEnabled: true
+ image: ":/icons/icons/ui/search.svg"
+ ripple: false
+ visible: !collapsed
+ width: 22
+
+ onClicked: {
+ var component = Qt.createComponent("qrc:/resources/qml/QuickSwitcher.qml");
+ if (component.status == Component.Ready) {
+ var quickSwitch = component.createObject(timelineRoot);
+ quickSwitch.open();
+ destroyOnClosed(quickSwitch);
+ } else {
+ console.error("Failed to create component: " + component.errorString());
+ }
+ }
+ }
+ ImageButton {
+ Layout.fillWidth: true
+ Layout.margins: Nheko.paddingMedium
+ ToolTip.delay: Nheko.tooltipDelay
+ ToolTip.text: qsTr("User settings")
+ ToolTip.visible: hovered
+ height: 22
+ hoverEnabled: true
+ image: ":/icons/icons/ui/settings.svg"
+ ripple: false
+ visible: !collapsed
+ width: 22
+
+ onClicked: mainWindow.push(userSettingsPage)
+ }
+ }
+ }
+ }
header: ColumnLayout {
spacing: 0
@@ -474,9 +162,11 @@ Page {
function openUserProfile() {
Nheko.updateUserProfile();
- var component = Qt.createComponent("qrc:/qml/dialogs/UserProfile.qml")
+ var component = Qt.createComponent("qrc:/resources/qml/dialogs/UserProfile.qml");
if (component.status == Component.Ready) {
- var userProfile = component.createObject(timelineRoot, {"profile": Nheko.currentUser});
+ var userProfile = component.createObject(timelineRoot, {
+ "profile": Nheko.currentUser
+ });
userProfile.show();
timelineRoot.destroyOnClose(userProfile);
} else {
@@ -484,55 +174,15 @@ Page {
}
}
-
- Layout.fillWidth: true
Layout.alignment: Qt.AlignBottom
+ Layout.fillWidth: true
+ Layout.minimumHeight: 40
//Layout.preferredHeight: userInfoGrid.implicitHeight + 2 * Nheko.paddingMedium
padding: Nheko.paddingMedium
- Layout.minimumHeight: 40
- background: Rectangle {color: Nheko.colors.window}
-
- InputDialog {
- id: statusDialog
-
- title: qsTr("Status Message")
- prompt: qsTr("Enter your status message:")
- onAccepted: function(text) {
- Nheko.setStatusMessage(text);
- }
+ background: Rectangle {
+ color: palette.window
}
-
- Platform.Menu {
- id: userInfoMenu
-
- Platform.MenuItem {
- text: qsTr("Profile settings")
- onTriggered: userInfoPanel.openUserProfile()
- }
-
- Platform.MenuItem {
- text: qsTr("Set status message")
- onTriggered: statusDialog.show()
- }
-
- }
-
- TapHandler {
- margin: -Nheko.paddingSmall
- acceptedButtons: Qt.LeftButton
- onSingleTapped: userInfoPanel.openUserProfile()
- onLongPressed: userInfoMenu.open()
- gesturePolicy: TapHandler.ReleaseWithinBounds
- }
-
- TapHandler {
- margin: -Nheko.paddingSmall
- acceptedButtons: Qt.RightButton
- onSingleTapped: userInfoMenu.open()
- gesturePolicy: TapHandler.ReleaseWithinBounds
- }
-
contentItem: RowLayout {
id: userInfoGrid
@@ -544,91 +194,123 @@ Page {
id: avatar
Layout.alignment: Qt.AlignVCenter
- Layout.preferredWidth: fontMetrics.lineSpacing * 2
Layout.preferredHeight: fontMetrics.lineSpacing * 2
- url: (userInfoGrid.profile ? userInfoGrid.profile.avatarUrl : "").replace("mxc://", "image://MxcImage/")
+ Layout.preferredWidth: fontMetrics.lineSpacing * 2
displayName: userInfoGrid.profile ? userInfoGrid.profile.displayName : ""
- userid: userInfoGrid.profile ? userInfoGrid.profile.userid : ""
enabled: false
+ url: (userInfoGrid.profile ? userInfoGrid.profile.avatarUrl : "").replace("mxc://", "image://MxcImage/")
+ userid: userInfoGrid.profile ? userInfoGrid.profile.userid : ""
}
-
ColumnLayout {
id: col
- visible: !collapsed
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
- width: parent.width - avatar.width - logoutButton.width - Nheko.paddingMedium * 2
Layout.preferredWidth: parent.width - avatar.width - logoutButton.width - Nheko.paddingMedium * 2
spacing: 0
+ visible: !collapsed
+ width: parent.width - avatar.width - logoutButton.width - Nheko.paddingMedium * 2
ElidedLabel {
Layout.alignment: Qt.AlignBottom
+ elideWidth: col.width
font.pointSize: fontMetrics.font.pointSize * 1.1
font.weight: Font.DemiBold
fullText: userInfoGrid.profile ? userInfoGrid.profile.displayName : ""
- elideWidth: col.width
}
-
ElidedLabel {
Layout.alignment: Qt.AlignTop
- color: Nheko.colors.buttonText
- font.pointSize: fontMetrics.font.pointSize * 0.9
+ color: palette.buttonText
elideWidth: col.width
+ font.pointSize: fontMetrics.font.pointSize * 0.9
fullText: userInfoGrid.profile ? userInfoGrid.profile.userid : ""
}
-
}
-
Item {
}
-
ImageButton {
id: logoutButton
- visible: !collapsed
Layout.alignment: Qt.AlignVCenter
- Layout.preferredWidth: fontMetrics.lineSpacing * 2
Layout.preferredHeight: fontMetrics.lineSpacing * 2
- image: ":/icons/icons/ui/power-off.svg"
- ToolTip.visible: hovered
+ Layout.preferredWidth: fontMetrics.lineSpacing * 2
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: qsTr("Logout")
+ ToolTip.visible: hovered
+ image: ":/icons/icons/ui/power-off.svg"
+ visible: !collapsed
+
onClicked: Nheko.openLogoutDialog()
}
-
}
- }
+ InputDialog {
+ id: statusDialog
+ prompt: qsTr("Enter your status message:")
+ title: qsTr("Status Message")
+
+ onAccepted: function (text) {
+ Nheko.setStatusMessage(text);
+ }
+ }
+ Platform.Menu {
+ id: userInfoMenu
+
+ Platform.MenuItem {
+ text: qsTr("Profile settings")
+
+ onTriggered: userInfoPanel.openUserProfile()
+ }
+ Platform.MenuItem {
+ text: qsTr("Set status message")
+
+ onTriggered: statusDialog.show()
+ }
+ }
+ TapHandler {
+ acceptedButtons: Qt.LeftButton
+ gesturePolicy: TapHandler.ReleaseWithinBounds
+ margin: -Nheko.paddingSmall
+
+ onLongPressed: userInfoMenu.open()
+ onSingleTapped: userInfoPanel.openUserProfile()
+ }
+ TapHandler {
+ acceptedButtons: Qt.RightButton
+ gesturePolicy: TapHandler.ReleaseWithinBounds
+ margin: -Nheko.paddingSmall
+
+ onSingleTapped: userInfoMenu.open()
+ }
+ }
Rectangle {
+ Layout.fillWidth: true
color: Nheko.theme.separator
height: 2
- Layout.fillWidth: true
}
-
Rectangle {
id: unverifiedStuffBubble
- color: Qt.lighter(Nheko.theme.orange, verifyButtonHovered.hovered ? 1.2 : 1)
Layout.fillWidth: true
+ color: Qt.lighter(Nheko.theme.orange, verifyButtonHovered.hovered ? 1.2 : 1)
implicitHeight: explanation.height + Nheko.paddingMedium * 2
visible: SelfVerificationStatus.status != SelfVerificationStatus.AllVerified
RowLayout {
id: unverifiedStuffBubbleContainer
- width: parent.width
height: explanation.height + Nheko.paddingMedium * 2
spacing: 0
+ width: parent.width
Label {
id: explanation
+ Layout.fillWidth: true
Layout.margins: Nheko.paddingMedium
Layout.rightMargin: Nheko.paddingSmall
- color: Nheko.colors.buttonText
- Layout.fillWidth: true
+ color: palette.buttonText
text: {
switch (SelfVerificationStatus.status) {
case SelfVerificationStatus.NoMasterKey:
@@ -647,34 +329,32 @@ Page {
textFormat: Text.PlainText
wrapMode: Text.Wrap
}
-
ImageButton {
id: closeUnverifiedBubble
- Layout.rightMargin: Nheko.paddingMedium
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
- hoverEnabled: true
- width: fontMetrics.font.pixelSize
- height: fontMetrics.font.pixelSize
- image: ":/icons/icons/ui/dismiss.svg"
- ToolTip.visible: closeUnverifiedBubble.hovered
+ Layout.rightMargin: Nheko.paddingMedium
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: qsTr("Close")
+ ToolTip.visible: closeUnverifiedBubble.hovered
+ height: fontMetrics.font.pixelSize
+ hoverEnabled: true
+ image: ":/icons/icons/ui/dismiss.svg"
+ width: fontMetrics.font.pixelSize
+
onClicked: unverifiedStuffBubble.visible = false
}
-
}
-
HoverHandler {
id: verifyButtonHovered
- enabled: !closeUnverifiedBubble.hovered
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
- }
-
- TapHandler {
enabled: !closeUnverifiedBubble.hovered
+ }
+ TapHandler {
acceptedButtons: Qt.LeftButton
+ enabled: !closeUnverifiedBubble.hovered
+
onSingleTapped: {
if (SelfVerificationStatus.status == SelfVerificationStatus.UnverifiedDevices)
SelfVerificationStatus.verifyUnverifiedDevices();
@@ -682,151 +362,431 @@ Page {
SelfVerificationStatus.statusChanged();
}
}
-
}
-
Rectangle {
+ Layout.fillWidth: true
color: Nheko.theme.separator
height: 1
- Layout.fillWidth: true
visible: unverifiedStuffBubble.visible
}
-
}
- footer: ColumnLayout {
- spacing: 0
-
- Rectangle {
- color: Nheko.theme.separator
- height: 1
- Layout.fillWidth: true
+ // HACK: https://bugreports.qt.io/browse/QTBUG-83972, qtwayland cannot auto hide menu
+ Connections {
+ function onHideMenu() {
+ userInfoMenu.close();
+ roomContextMenu.close();
}
- Pane {
- Layout.fillWidth: true
- Layout.alignment: Qt.AlignBottom
- Layout.minimumHeight: 40
+ target: MainWindow
+ }
+ Component {
+ id: roomDirectoryComponent
- horizontalPadding: Nheko.paddingMedium
- verticalPadding: 0
+ RoomDirectory {
+ }
+ }
+ Component {
+ id: createRoomComponent
- background: Rectangle {color: Nheko.colors.window}
- contentItem: RowLayout {
- id: buttonRow
+ CreateRoom {
+ }
+ }
+ Component {
+ id: createDirectComponent
- ImageButton {
- Layout.fillWidth: true
- hoverEnabled: true
- width: 22
- height: 22
- image: ":/icons/icons/ui/add-square-button.svg"
- ToolTip.visible: hovered
- ToolTip.delay: Nheko.tooltipDelay
- ToolTip.text: qsTr("Start a new chat")
- Layout.margins: Nheko.paddingMedium
- onClicked: roomJoinCreateMenu.open(parent)
+ CreateDirect {
+ }
+ }
+ ListView {
+ id: roomlist
- Platform.Menu {
- id: roomJoinCreateMenu
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: parent.height
+ model: Rooms
- Platform.MenuItem {
- text: qsTr("Join a room")
- onTriggered: Nheko.openJoinRoomDialog()
- }
+ //reuseItems: true
+ ScrollBar.vertical: ScrollBar {
+ id: scrollbar
- Platform.MenuItem {
- text: qsTr("Create a new room")
- onTriggered: {
- var createRoom = createRoomComponent.createObject(timelineRoot);
- createRoom.show();
- timelineRoot.destroyOnClose(createRoom);
- }
- }
+ parent: !collapsed && Settings.scrollbarsInRoomlist ? roomlist : null
+ }
+ delegate: ItemDelegate {
+ id: roomItem
- Platform.MenuItem {
- text: qsTr("Start a direct chat")
- onTriggered: {
- var createDirect = createDirectComponent.createObject(timelineRoot);
- createDirect.show();
- timelineRoot.destroyOnClose(createDirect);
- }
- }
+ required property string avatarUrl
+ property color backgroundColor: palette.window
+ property color bubbleBackground: palette.highlight
+ property color bubbleText: palette.highlightedText
+ required property string directChatOtherUserId
+ required property bool hasLoudNotification
+ required property bool hasUnreadMessages
+ property color importantText: palette.text
+ required property bool isDirect
+ required property bool isInvite
+ required property bool isSpace
+ required property string lastMessage
+ required property int notificationCount
+ required property string roomId
+ required property string roomName
+ required property var tags
+ required property string time
+ property color unimportantText: palette.buttonText
- Platform.MenuItem {
- text: qsTr("Create a new community")
- onTriggered: {
- var createRoom = createRoomComponent.createObject(timelineRoot, { "space": true });
- createRoom.show();
- timelineRoot.destroyOnClose(createRoom);
- }
- }
+ ToolTip.delay: Nheko.tooltipDelay
+ ToolTip.text: roomName
+ 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: backgroundColor
+ }
+ states: [
+ State {
+ name: "highlight"
+ when: roomItem.hovered && !((Rooms.currentRoom && roomId == Rooms.currentRoom.roomId) || Rooms.currentRoomPreview.roomid == roomId)
+
+ PropertyChanges {
+ backgroundColor: palette.dark
+ bubbleBackground: palette.highlight
+ bubbleText: palette.highlightedText
+ importantText: palette.brightText
+ target: roomItem
+ unimportantText: palette.brightText
}
+ },
+ State {
+ name: "selected"
+ when: (Rooms.currentRoom && roomId == Rooms.currentRoom.roomId) || Rooms.currentRoomPreview.roomid == roomId
- }
-
- ImageButton {
- visible: !collapsed
- Layout.fillWidth: true
- hoverEnabled: true
- width: 22
- height: 22
- image: ":/icons/icons/ui/room-directory.svg"
- ToolTip.visible: hovered
- ToolTip.delay: Nheko.tooltipDelay
- ToolTip.text: qsTr("Room directory")
- Layout.margins: Nheko.paddingMedium
- onClicked: {
- var win = roomDirectoryComponent.createObject(timelineRoot);
- win.show();
- timelineRoot.destroyOnClose(win);
+ PropertyChanges {
+ backgroundColor: palette.highlight
+ bubbleBackground: palette.highlightedText
+ bubbleText: palette.highlight
+ importantText: palette.highlightedText
+ target: roomItem
+ unimportantText: palette.highlightedText
}
}
+ ]
- ImageButton {
- visible: !collapsed
- Layout.fillWidth: true
- hoverEnabled: true
- ripple: false
- width: 22
- height: 22
- image: ":/icons/icons/ui/search.svg"
- ToolTip.visible: hovered
- ToolTip.delay: Nheko.tooltipDelay
- ToolTip.text: qsTr("Search rooms (Ctrl+K)")
- Layout.margins: Nheko.paddingMedium
- onClicked: {
- var component = Qt.createComponent("qrc:/qml/QuickSwitcher.qml")
- if (component.status == Component.Ready) {
- var quickSwitch = component.createObject(timelineRoot);
- quickSwitch.open();
- destroyOnClosed(quickSwitch);
- } else {
- console.error("Failed to create component: " + component.errorString());
- }
- }
- }
-
- ImageButton {
- visible: !collapsed
- Layout.fillWidth: true
- hoverEnabled: true
- ripple: false
- width: 22
- height: 22
- image: ":/icons/icons/ui/settings.svg"
- ToolTip.visible: hovered
- ToolTip.delay: Nheko.tooltipDelay
- ToolTip.text: qsTr("User settings")
- Layout.margins: Nheko.paddingMedium
- onClicked: mainWindow.push(userSettingsPage);
- }
-
+ onClicked: {
+ console.log("tapped " + roomId);
+ if (!Rooms.currentRoom || Rooms.currentRoom.roomId !== roomId)
+ Rooms.setCurrentRoom(roomId);
+ else
+ Rooms.resetCurrentRoom();
+ }
+ onPressAndHold: {
+ if (!isInvite)
+ roomContextMenu.show(roomId, tags);
}
+ Ripple {
+ color: Qt.rgba(palette.dark.r, palette.dark.g, palette.dark.b, 0.5)
+ }
+
+ // NOTE(Nico): We want to prevent the touch areas from overlapping. For some reason we need to add 1px of padding for that...
+ Item {
+ anchors.fill: parent
+ anchors.margins: 1
+
+ TapHandler {
+ acceptedButtons: Qt.RightButton
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
+ gesturePolicy: TapHandler.ReleaseWithinBounds
+
+ onSingleTapped: {
+ if (!TimelineManager.isInvite)
+ roomContextMenu.show(roomId, tags);
+ }
+ }
+ }
+ RowLayout {
+ anchors.fill: parent
+ anchors.margins: Nheko.paddingMedium
+ spacing: Nheko.paddingMedium
+
+ Avatar {
+ id: avatar
+
+ Layout.alignment: Qt.AlignVCenter
+ displayName: roomName
+ enabled: false
+ height: avatarSize
+ roomid: roomId
+ url: avatarUrl.replace("mxc://", "image://MxcImage/")
+ userid: isDirect ? directChatOtherUserId : ""
+ width: avatarSize
+
+ NotificationBubble {
+ id: collapsedNotificationBubble
+
+ anchors.bottom: parent.bottom
+ anchors.margins: -Nheko.paddingSmall
+ anchors.right: parent.right
+ bubbleBackgroundColor: roomItem.bubbleBackground
+ bubbleTextColor: roomItem.bubbleText
+ hasLoudNotification: roomItem.hasLoudNotification
+ mayBeVisible: collapsed && (isSpace ? Settings.spaceNotifications : true)
+ notificationCount: roomItem.notificationCount
+ }
+ }
+ ColumnLayout {
+ id: textContent
+
+ Layout.alignment: Qt.AlignLeft
+ Layout.minimumWidth: 100
+ Layout.preferredWidth: parent.width - avatar.width
+ height: avatar.height
+ spacing: Nheko.paddingSmall
+ visible: !collapsed
+ width: roomItem.width - avatar.width
+
+ Item {
+ id: titleRow
+
+ Layout.alignment: Qt.AlignTop
+ Layout.fillWidth: true
+ Layout.preferredHeight: subtitleText.implicitHeight
+
+ ElidedLabel {
+ id: titleText
+
+ anchors.left: parent.left
+ color: roomItem.importantText
+ elideWidth: parent.width - (timestamp.visible ? timestamp.implicitWidth : 0) - (spaceNotificationBubble.visible ? spaceNotificationBubble.implicitWidth : 0)
+ fullText: TimelineManager.htmlEscape(roomName)
+ textFormat: Text.RichText
+ }
+ Label {
+ id: timestamp
+
+ anchors.baseline: titleText.baseline
+ anchors.right: parent.right
+ color: roomItem.unimportantText
+ font.pixelSize: fontMetrics.font.pixelSize * 0.9
+ text: time
+ visible: !isInvite && !isSpace
+ }
+ NotificationBubble {
+ id: spaceNotificationBubble
+
+ anchors.right: parent.right
+ bubbleBackgroundColor: roomItem.bubbleBackground
+ bubbleTextColor: roomItem.bubbleText
+ hasLoudNotification: roomItem.hasLoudNotification
+ mayBeVisible: !collapsed && (isSpace ? Settings.spaceNotifications : false)
+ notificationCount: roomItem.notificationCount
+ parent: isSpace ? titleRow : subtextRow
+ }
+ }
+ Item {
+ id: subtextRow
+
+ Layout.alignment: Qt.AlignBottom
+ Layout.fillWidth: true
+ Layout.preferredHeight: subtitleText.implicitHeight
+ visible: !isSpace
+
+ ElidedLabel {
+ id: subtitleText
+
+ anchors.left: parent.left
+ color: roomItem.unimportantText
+ elideWidth: subtextRow.width - (subtextNotificationBubble.visible ? subtextNotificationBubble.implicitWidth : 0)
+ font.pixelSize: fontMetrics.font.pixelSize * 0.9
+ fullText: TimelineManager.htmlEscape(lastMessage)
+ textFormat: Text.RichText
+ }
+ NotificationBubble {
+ id: subtextNotificationBubble
+
+ anchors.baseline: subtitleText.baseline
+ anchors.right: parent.right
+ bubbleBackgroundColor: roomItem.bubbleBackground
+ bubbleTextColor: roomItem.bubbleText
+ hasLoudNotification: roomItem.hasLoudNotification
+ mayBeVisible: !collapsed
+ notificationCount: roomItem.notificationCount
+ }
+ }
+ }
+ }
+ Rectangle {
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ color: palette.highlight
+ height: parent.height - Nheko.paddingSmall * 2
+ visible: hasUnreadMessages
+ width: 3
+ }
}
- }
+ Connections {
+ function onCurrentRoomChanged() {
+ if (Rooms.currentRoom)
+ roomlist.positionViewAtIndex(Rooms.roomidToIndex(Rooms.currentRoom.roomId), ListView.Contain);
+ }
+ target: Rooms
+ }
+ Component {
+ id: roomWindowComponent
+
+ ApplicationWindow {
+ id: roomWindowW
+
+ property var room: null
+ property var roomPreview: null
+
+ color: palette.window
+ height: 650
+ minimumHeight: 150
+ minimumWidth: 150
+ title: room.plainRoomName
+ width: 420
+
+ Component.onCompleted: {
+ MainWindow.addPerRoomWindow(room.roomId || roomPreview.roomid, roomWindowW);
+ Nheko.setTransientParent(roomWindowW, null);
+ }
+ Component.onDestruction: MainWindow.removePerRoomWindow(room.roomId || roomPreview.roomid, roomWindowW)
+ onActiveChanged: {
+ room.lastReadIdOnWindowFocus();
+ }
+
+ //flags: Qt.Window | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
+ Shortcut {
+ sequence: StandardKey.Cancel
+
+ onActivated: roomWindowW.close()
+ }
+ TimelineView {
+ id: timeline
+
+ anchors.fill: parent
+ privacyScreen: privacyScreen
+ room: roomWindowW.room
+ roomPreview: roomWindowW.roomPreview.roomid ? roomWindowW.roomPreview : null
+ }
+ PrivacyScreen {
+ id: privacyScreen
+
+ anchors.fill: parent
+ screenTimeout: Settings.privacyScreenTimeout
+ timelineRoot: timeline
+ visible: Settings.privacyScreen
+ windowTarget: roomWindowW
+ }
+ }
+ }
+ Component {
+ id: nestedSpaceMenuLevel
+
+ SpaceMenuLevel {
+ childMenu: rootSpaceMenu.childMenu
+ roomid: roomContextMenu.roomid
+ }
+ }
+ Platform.Menu {
+ id: roomContextMenu
+
+ property string roomid
+ property var tags
+
+ function show(roomid_, tags_) {
+ roomid = roomid_;
+ tags = tags_;
+ open();
+ }
+
+ InputDialog {
+ id: newTag
+
+ prompt: qsTr("Enter the tag you want to use:")
+ title: qsTr("New tag")
+
+ onAccepted: function (text) {
+ Rooms.toggleTag(roomContextMenu.roomid, "u." + text, true);
+ }
+ }
+ Platform.MenuItem {
+ text: qsTr("Open separately")
+
+ onTriggered: {
+ var roomWindow = roomWindowComponent.createObject(null, {
+ "room": Rooms.getRoomById(roomContextMenu.roomid),
+ "roomPreview": Rooms.getRoomPreviewById(roomContextMenu.roomid)
+ });
+ roomWindow.showNormal();
+ destroyOnClose(roomWindow);
+ }
+ }
+ Platform.MenuItem {
+ text: qsTr("Room settings")
+
+ onTriggered: TimelineManager.openRoomSettings(roomContextMenu.roomid)
+ }
+ Platform.MenuItem {
+ text: qsTr("Leave room")
+
+ onTriggered: TimelineManager.openLeaveRoomDialog(roomContextMenu.roomid)
+ }
+ Platform.MenuItem {
+ text: qsTr("Copy room link")
+
+ onTriggered: Rooms.copyLink(roomContextMenu.roomid)
+ }
+ Platform.Menu {
+ id: tagsMenu
+
+ title: qsTr("Tag room as:")
+
+ Instantiator {
+ model: Communities.tagsWithDefault
+
+ delegate: Platform.MenuItem {
+ property string t: modelData
+
+ checkable: true
+ checked: roomContextMenu.tags !== undefined && roomContextMenu.tags.includes(t)
+ text: {
+ switch (t) {
+ case "m.favourite":
+ return qsTr("Favourite");
+ case "m.lowpriority":
+ return qsTr("Low priority");
+ case "m.server_notice":
+ return qsTr("Server notice");
+ default:
+ return t.substring(2);
+ }
+ }
+
+ onTriggered: Rooms.toggleTag(roomContextMenu.roomid, t, checked)
+ }
+
+ onObjectAdded: (index, object) => tagsMenu.insertItem(index, object)
+ onObjectRemoved: (index, object) => tagsMenu.removeItem(object)
+ }
+ Platform.MenuItem {
+ text: qsTr("Create new tag...")
+
+ onTriggered: newTag.show()
+ }
+ }
+ SpaceMenuLevel {
+ id: rootSpaceMenu
+
+ childMenu: nestedSpaceMenuLevel
+ position: -1
+ roomid: roomContextMenu.roomid
+ title: qsTr("Add or remove from community...")
+ }
+ }
+ }
}
diff --git a/resources/qml/Root.qml b/resources/qml/Root.qml
index 4b71af37..e26b386a 100644
--- a/resources/qml/Root.qml
+++ b/resources/qml/Root.qml
@@ -10,30 +10,23 @@ import "./pages"
import "./voip"
import "./ui"
import Qt.labs.platform 1.1 as Platform
-import QtQuick 2.15
-import QtQuick.Controls 2.15
-import QtQuick.Layouts 1.3
-import QtQuick.Window 2.15
-import im.nheko 1.0
-import im.nheko.EmojiModel 1.0
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Window
+import im.nheko
Pane {
id: timelineRoot
- palette: Nheko.colors
- background: null
- padding: 0
-
- FontMetrics {
- id: fontMetrics
+ function destroyOnClose(obj) {
+ if (obj.closing != undefined)
+ obj.closing.connect(() => obj.destroy(1000));
+ else if (obj.aboutToHide != undefined)
+ obj.aboutToHide.connect(() => obj.destroy(1000));
}
-
- RoomDirectoryModel {
- id: publicRooms
- }
-
- UserDirectoryModel {
- id: userDirectory
+ function destroyOnClosed(obj) {
+ obj.aboutToHide.connect(() => obj.destroy(1000));
}
//Timer {
@@ -42,54 +35,49 @@ Pane {
// running: true
// repeat: true
//}
-
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) {
var dialog = component.createObject(timelineRoot, {
- "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
- });
+ "roomSettings": settings
+ });
dialog.show();
destroyOnClose(dialog);
} else {
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) {
- var component = Qt.createComponent("qrc:/qml/dialogs/AllowedRoomsSettingsDialog.qml")
+ var component = Qt.createComponent("qrc:/resources/qml/dialogs/AllowedRoomsSettingsDialog.qml");
if (component.status == Component.Ready) {
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();
destroyOnClose(dialog);
} else {
@@ -97,23 +85,37 @@ Pane {
}
}
+ background: null
+ padding: 0
+
+ FontMetrics {
+ id: fontMetrics
+
+ }
+ UserDirectoryModel {
+ id: userDirectory
+
+ }
+ RoomDirectoryModel {
+ id: publicRooms
+
+ }
Component {
id: readReceiptsDialog
ReadReceipts {
}
-
}
-
Shortcut {
sequence: StandardKey.Quit
+
onActivated: Qt.quit()
}
-
Shortcut {
sequence: "Ctrl+K"
+
onActivated: {
- var component = Qt.createComponent("qrc:/qml/QuickSwitcher.qml")
+ var component = Qt.createComponent("qrc:/resources/qml/QuickSwitcher.qml");
if (component.status == Component.Ready) {
var quickSwitch = component.createObject(timelineRoot);
quickSwitch.open();
@@ -123,37 +125,25 @@ Pane {
}
}
}
-
Shortcut {
// Add alternative shortcut, because sometimes Alt+A is stolen by the TextEdit
sequences: ["Alt+A", "Ctrl+Shift+A"]
+
onActivated: Rooms.nextRoomWithActivity()
}
-
Shortcut {
sequence: "Ctrl+Down"
+
onActivated: Rooms.nextRoom()
}
-
Shortcut {
sequence: "Ctrl+Up"
+
onActivated: Rooms.previousRoom()
}
-
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() {
- var component = Qt.createComponent("qrc:/qml/dialogs/JoinRoomDialog.qml")
+ var component = Qt.createComponent("qrc:/resources/qml/dialogs/JoinRoomDialog.qml");
if (component.status == Component.Ready) {
var dialog = component.createObject(timelineRoot);
dialog.show();
@@ -162,11 +152,22 @@ Pane {
console.error("Failed to create component: " + component.errorString());
}
}
-
- function onShowRoomJoinPrompt(summary) {
- var component = Qt.createComponent("qrc:/qml/dialogs/ConfirmJoinRoomDialog.qml")
+ function onOpenLogoutDialog() {
+ var component = Qt.createComponent("qrc:/resources/qml/dialogs/LogoutDialog.qml");
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();
destroyOnClose(dialog);
} else {
@@ -176,12 +177,13 @@ Pane {
target: Nheko
}
-
Connections {
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) {
- var dialog = component.createObject(timelineRoot, {"flow": flow});
+ var dialog = component.createObject(timelineRoot, {
+ "flow": flow
+ });
dialog.show();
destroyOnClose(dialog);
} else {
@@ -191,101 +193,71 @@ Pane {
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 {
- 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) {
- var component = Qt.createComponent("qrc:/qml/dialogs/InviteDialog.qml")
+ var component = Qt.createComponent("qrc:/resources/qml/dialogs/InviteDialog.qml");
if (component.status == Component.Ready) {
var dialog = component.createObject(timelineRoot, {
- "invitees": invitees
- });
+ "invitees": invitees
+ });
dialog.show();
destroyOnClose(dialog);
} else {
console.error("Failed to create component: " + component.errorString());
}
}
-
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) {
var dialog = component.createObject(timelineRoot, {
- "roomId": roomid,
- "reason": reason
- });
+ "roomId": roomid,
+ "reason": reason
+ });
dialog.open();
destroyOnClose(dialog);
} else {
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) {
- var component = Qt.createComponent("qrc:/qml/dialogs/ImageOverlay.qml")
+ var component = Qt.createComponent("qrc:/resources/qml/dialogs/ImageOverlay.qml");
if (component.status == Component.Ready) {
var dialog = component.createObject(timelineRoot, {
"room": room,
@@ -293,22 +265,33 @@ Pane {
"url": url,
"originalWidth": originalWidth ?? 0,
"proportionalHeight": proportionalHeight ?? 0
- }
- );
+ });
dialog.showFullScreen();
destroyOnClose(dialog);
} else {
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
}
-
Connections {
function onNewInviteState() {
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) {
var dialog = component.createObject(timelineRoot);
dialog.open();
@@ -321,141 +304,97 @@ Pane {
target: CallManager
}
-
SelfVerificationCheck {
}
-
InputDialog {
id: uiaPassPrompt
echoMode: TextInput.Password
- title: UIA.title
prompt: qsTr("Please enter your login password to continue:")
- onAccepted: (t) => {
+ title: UIA.title
+
+ onAccepted: t => {
return UIA.continuePassword(t);
}
}
-
InputDialog {
id: uiaEmailPrompt
- title: UIA.title
prompt: qsTr("Please enter a valid email address to continue:")
- onAccepted: (t) => {
+ title: UIA.title
+
+ onAccepted: t => {
return UIA.continueEmail(t);
}
}
-
PhoneNumberInputDialog {
id: uiaPhoneNumberPrompt
- title: UIA.title
prompt: qsTr("Please enter a valid phone number to continue:")
+ title: UIA.title
+
onAccepted: (p, t) => {
return UIA.continuePhoneNumber(p, t);
}
}
-
InputDialog {
id: uiaTokenPrompt
- title: UIA.title
prompt: qsTr("Please enter the token which has been sent to you:")
- onAccepted: (t) => {
+ title: UIA.title
+
+ onAccepted: t => {
return UIA.submit3pidToken(t);
}
}
-
Platform.MessageDialog {
id: uiaErrorDialog
buttons: Platform.MessageDialog.Ok
}
-
Platform.MessageDialog {
id: uiaConfirmationLinkDialog
buttons: Platform.MessageDialog.Ok
text: qsTr("Wait for the confirmation link to arrive, then continue.")
+
onAccepted: UIA.continue3pidReceived()
}
-
Connections {
- function onPassword() {
- console.log("UIA: password needed");
- uiaPassPrompt.show();
- }
-
- function onEmail() {
- uiaEmailPrompt.show();
- }
-
- function onPhoneNumber() {
- uiaPhoneNumberPrompt.show();
- }
-
- function onPrompt3pidToken() {
- uiaTokenPrompt.show();
- }
-
function onConfirm3pidToken() {
uiaConfirmationLinkDialog.open();
}
-
+ function onEmail() {
+ uiaEmailPrompt.show();
+ }
function onError(msg) {
uiaErrorDialog.text = msg;
uiaErrorDialog.open();
}
+ function onPassword() {
+ console.log("UIA: password needed");
+ uiaPassPrompt.show();
+ }
+ function onPhoneNumber() {
+ uiaPhoneNumberPrompt.show();
+ }
+ function onPrompt3pidToken() {
+ uiaTokenPrompt.show();
+ }
target: UIA
}
-
StackView {
id: mainWindow
- anchors.fill: parent
- initialItem: welcomePage
-
- 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
- }
- }
- }
+ property Transition popEnterOrg
+ property Transition popExitOrg
// for some reason direct bindings to a hidden StackView don't work, so manually store and restore here.
property Transition pushEnterOrg
property Transition pushExitOrg
- property Transition popEnterOrg
- property Transition popExitOrg
property Transition replaceEnterOrg
property Transition replaceExitOrg
- Component.onCompleted: {
- pushEnterOrg = pushEnter;
- popEnterOrg = popEnter;
- replaceEnterOrg = replaceEnter;
- pushExitOrg = pushExit;
- popExitOrg = popExit;
- replaceExitOrg = replaceExit;
-
- updateTrans()
- }
function updateTrans() {
pushEnter = Settings.reducedMotion ? reducedMotionTransitionEnter : pushEnterOrg;
@@ -466,65 +405,104 @@ Pane {
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 {
- target: Settings
function onReducedMotionChanged() {
mainWindow.updateTrans();
}
+
+ target: Settings
}
}
-
Component {
id: welcomePage
WelcomePage {
}
}
-
Component {
id: chatPage
ChatPage {
}
}
-
Component {
id: loginPage
LoginPage {
}
}
-
Component {
id: registerPage
RegisterPage {
}
}
-
Component {
id: userSettingsPage
UserSettingsPage {
}
+ }
+ Snackbar {
+ id: snackbar
}
-
-
- Snackbar { id: snackbar }
-
Connections {
- function onSwitchToChatPage() {
- mainWindow.replace(null, chatPage);
- }
- function onSwitchToLoginPage(error) {
- mainWindow.replace(welcomePage, {}, loginPage, {"error": error}, StackView.PopTransition);
- }
function onShowNotification(msg) {
snackbar.showNotification(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
}
-
}
diff --git a/resources/qml/ScrollHelper.qml b/resources/qml/ScrollHelper.qml
deleted file mode 100644
index 04d060ec..00000000
--- a/resources/qml/ScrollHelper.qml
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (C) 2016 Michael Bohlender,
-// Copyright (C) 2017 Christian Mollekopf,
-// 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();
- }
- }
-
-}
diff --git a/resources/qml/SelfVerificationCheck.qml b/resources/qml/SelfVerificationCheck.qml
index 4f2d9202..1752df0e 100644
--- a/resources/qml/SelfVerificationCheck.qml
+++ b/resources/qml/SelfVerificationCheck.qml
@@ -10,22 +10,32 @@ import QtQuick.Layouts 1.3
import im.nheko 1.0
Item {
- visible: false
enabled: false
+ visible: false
Dialog {
id: showRecoverKeyDialog
property string recoveryKey: ""
- parent: Overlay.overlay
anchors.centerIn: parent
- height: content.height + implicitFooterHeight + implicitHeaderHeight
- width: content.width
- padding: 0
- modal: true
- standardButtons: Dialog.Ok
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 {
id: content
@@ -33,45 +43,33 @@ Item {
spacing: 0
Label {
+ Layout.fillWidth: true
Layout.margins: Nheko.paddingMedium
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!")
- color: Nheko.colors.text
wrapMode: Text.Wrap
}
-
TextEdit {
- Layout.maximumWidth: (Overlay.overlay ? Overlay.overlay.width : 400) - Nheko.paddingMedium * 4
Layout.alignment: Qt.AlignHCenter
+ Layout.maximumWidth: (Overlay.overlay ? Overlay.overlay.width : 400) - Nheko.paddingMedium * 4
+ color: palette.text
+ font.bold: true
horizontalAlignment: TextEdit.AlignHCenter
- verticalAlignment: TextEdit.AlignVCenter
readOnly: true
selectByMouse: true
text: showRecoverKeyDialog.recoveryKey
- color: Nheko.colors.text
- font.bold: true
+ verticalAlignment: TextEdit.AlignVCenter
wrapMode: TextEdit.Wrap
}
-
}
-
- background: Rectangle {
- color: Nheko.colors.window
- border.color: Nheko.theme.separator
- border.width: 1
- radius: Nheko.paddingSmall
- }
-
}
-
P.MessageDialog {
id: successDialog
buttons: P.MessageDialog.Ok
text: qsTr("Encryption setup successfully")
}
-
P.MessageDialog {
id: failureDialog
@@ -80,85 +78,89 @@ Item {
buttons: P.MessageDialog.Ok
text: qsTr("Failed to setup encryption: %1").arg(errorMessage)
}
-
MainWindowDialog {
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)
GridLayout {
id: grid
- width: bootstrapCrosssigning.useableWidth
+ columnSpacing: 0
columns: 2
rowSpacing: 0
- columnSpacing: 0
+ width: bootstrapCrosssigning.useableWidth
z: 1
Label {
- Layout.margins: Nheko.paddingMedium
Layout.alignment: Qt.AlignHCenter
Layout.columnSpan: 2
+ Layout.margins: Nheko.paddingMedium
+ color: palette.text
font.pointSize: fontMetrics.font.pointSize * 2
text: qsTr("Setup Encryption")
- color: Nheko.colors.text
wrapMode: Text.Wrap
}
-
Label {
- Layout.margins: Nheko.paddingMedium
Layout.alignment: Qt.AlignLeft
Layout.columnSpan: 2
+ Layout.margins: Nheko.paddingMedium
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!")
- color: Nheko.colors.text
wrapMode: Text.Wrap
}
-
Label {
- Layout.margins: Nheko.paddingMedium
Layout.alignment: Qt.AlignLeft
Layout.columnSpan: 1
+ Layout.margins: Nheko.paddingMedium
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!"
- color: Nheko.colors.text
wrapMode: Text.Wrap
}
-
Item {
- Layout.margins: Nheko.paddingMedium
- Layout.preferredHeight: storeSecretsOnline.height
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.fillWidth: true
+ Layout.margins: Nheko.paddingMedium
+ Layout.preferredHeight: storeSecretsOnline.height
ToggleButton {
id: storeSecretsOnline
checked: true
+
onClicked: console.log("Store secrets toggled: " + checked)
}
-
}
-
Label {
- Layout.margins: Nheko.paddingMedium
Layout.alignment: Qt.AlignLeft
Layout.columnSpan: 1
- Layout.rowSpan: 2
+ Layout.margins: Nheko.paddingMedium
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.)"
- color: Nheko.colors.text
+ visible: storeSecretsOnline.checked
wrapMode: Text.Wrap
}
-
Item {
- Layout.margins: Nheko.paddingMedium
- Layout.topMargin: Nheko.paddingLarge
- Layout.preferredHeight: storeSecretsOnline.height
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
- Layout.rowSpan: usePassword.checked ? 1 : 2
Layout.fillWidth: true
+ Layout.margins: Nheko.paddingMedium
+ Layout.preferredHeight: storeSecretsOnline.height
+ Layout.rowSpan: usePassword.checked ? 1 : 2
+ Layout.topMargin: Nheko.paddingLarge
visible: storeSecretsOnline.checked
ToggleButton {
@@ -166,113 +168,108 @@ Item {
checked: false
}
-
}
-
MatrixTextField {
id: passwordField
- Layout.margins: Nheko.paddingMedium
- Layout.maximumWidth: Math.floor(grid.width / 2) - Nheko.paddingMedium * 2
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.columnSpan: 1
Layout.fillWidth: true
- visible: storeSecretsOnline.checked && usePassword.checked
- echoMode: TextInput.Password
- }
-
- Label {
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.columnSpan: 1
+ Layout.margins: Nheko.paddingMedium
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."
- color: Nheko.colors.text
wrapMode: Text.Wrap
}
-
Item {
- Layout.margins: Nheko.paddingMedium
- Layout.preferredHeight: storeSecretsOnline.height
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.fillWidth: true
+ Layout.margins: Nheko.paddingMedium
+ Layout.preferredHeight: storeSecretsOnline.height
ToggleButton {
id: useOnlineKeyBackup
checked: true
+
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 {
id: verifyMasterKey
+ // Workaround palettes not inheriting for popups
+ palette: timelineRoot.palette
standardButtons: Dialog.Cancel
GridLayout {
id: masterGrid
- width: verifyMasterKey.useableWidth
columns: 1
+ width: verifyMasterKey.useableWidth
z: 1
Label {
- Layout.margins: Nheko.paddingMedium
Layout.alignment: Qt.AlignHCenter
+ Layout.margins: Nheko.paddingMedium
+ color: palette.text
//Layout.columnSpan: 2
font.pointSize: fontMetrics.font.pointSize * 2
text: qsTr("Activate Encryption")
- color: Nheko.colors.text
wrapMode: Text.Wrap
}
-
Label {
- Layout.margins: Nheko.paddingMedium
Layout.alignment: Qt.AlignLeft
+ Layout.margins: Nheko.paddingMedium
//Layout.columnSpan: 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.")
- color: Nheko.colors.text
wrapMode: Text.Wrap
}
-
FlatButton {
Layout.alignment: Qt.AlignHCenter
text: qsTr("verify")
+
onClicked: {
SelfVerificationStatus.verifyMasterKey();
verifyMasterKey.close();
}
}
-
FlatButton {
- visible: SelfVerificationStatus.hasSSSS
Layout.alignment: Qt.AlignHCenter
text: qsTr("enter passphrase")
+ visible: SelfVerificationStatus.hasSSSS
+
onClicked: {
SelfVerificationStatus.verifyMasterKeyWithPassphrase();
verifyMasterKey.close();
}
}
-
}
-
}
-
Connections {
+ function onSetupCompleted() {
+ successDialog.open();
+ }
+ function onSetupFailed(m) {
+ failureDialog.errorMessage = m;
+ failureDialog.open();
+ }
+ function onShowRecoveryKey(key) {
+ showRecoverKeyDialog.recoveryKey = key;
+ showRecoverKeyDialog.open();
+ }
function onStatusChanged() {
console.log("STATUS CHANGED: " + SelfVerificationStatus.status);
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
}
-
}
diff --git a/resources/qml/StatusIndicator.qml b/resources/qml/StatusIndicator.qml
index 862f9d7a..4a305ac5 100644
--- a/resources/qml/StatusIndicator.qml
+++ b/resources/qml/StatusIndicator.qml
@@ -9,15 +9,9 @@ import im.nheko 1.0
ImageButton {
id: indicator
- required property int status
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: {
switch (status) {
case MtxEvent.Failed:
@@ -32,11 +26,11 @@ ImageButton {
return "";
}
}
- onClicked: {
- if (status == MtxEvent.Read)
- room.showReadReceipts(eventId);
-
- }
+ ToolTip.visible: hovered && status != MtxEvent.Empty
+ changeColorOnHover: (status == MtxEvent.Read)
+ cursor: (status == MtxEvent.Read) ? Qt.PointingHandCursor : Qt.ArrowCursor
+ height: 16
+ hoverEnabled: true
image: {
switch (status) {
case MtxEvent.Failed:
@@ -51,4 +45,10 @@ ImageButton {
return "";
}
}
+ width: 16
+
+ onClicked: {
+ if (status == MtxEvent.Read)
+ room.showReadReceipts(eventId);
+ }
}
diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml
index d9deefa0..16a31a3c 100644
--- a/resources/qml/TimelineRow.qml
+++ b/resources/qml/TimelineRow.qml
@@ -13,72 +13,44 @@ import im.nheko 1.0
AbstractButton {
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 body
- required property string formattedBody
+ required property string callType
+ required property int duration
+ required property int encryptionError
required property string eventId
required property string filename
required property string filesize
- required property string url
- required property string thumbnailUrl
- required property bool isOnlyEmoji
- required property bool isSender
- required property bool isEncrypted
+ required property string formattedBody
+ required property int index
required property bool isEditable
required property bool isEdited
+ required property bool isEncrypted
+ required property bool isOnlyEmoji
+ required property bool isSender
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 roomName
+ required property string roomTopic
+ required property int status
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 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
- 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 {
name: "dragging"
when: draghandler.active
@@ -86,265 +58,292 @@ AbstractButton {
transitions: Transition {
from: "dragging"
to: ""
+
PropertyAnimation {
- target: r
- properties: "x"
- easing.type: Easing.InOutQuad
- to: 0
duration: 100
+ easing.type: Easing.InOutQuad
+ properties: "x"
+ target: r
+ to: 0
}
}
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) {
- 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 {
- 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.leftMargin: Settings.smallAvatars ? 0 : (Nheko.avatarSize + 8) // align bubble with section header
+ height: parent.height
visible: threadId
width: 4
- height: parent.height
+
+ onClicked: room.thread = threadId
Rectangle {
id: threadLine
- color: TimelineManager.userColor(threadId, Nheko.colors.base)
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 {
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: Nheko.colors.base
- color: (Settings.bubbles && !isStateEvent) ? Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.2)) : "#00000000"
- radius: 4
- border.width: r.notificationlevel == MtxEvent.Highlight ? 1 : 0
+ property color bgColor: palette.base
+ property bool bubbleOnRight: isSender && Settings.bubbles
+ property int maxWidth: (parent.width - (Settings.smallAvatars || isStateEvent ? 0 : Nheko.avatarSize + 8)) * (Settings.bubbles && !isStateEvent ? 0.9 : 1)
+ property color userColor: TimelineManager.userColor(userId, palette.base)
+
+ 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.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 {
+ id: msg
+
+ columnSpacing: 2
+ columns: Settings.bubbles ? 1 : 2
+ rowSpacing: 0
+ rows: Settings.bubbles ? 3 : 2
+
anchors {
left: parent.left
- top: parent.top
- right: parent.right
- margins: (Settings.bubbles && ! isStateEvent)? 4 : 2
leftMargin: 4
+ margins: (Settings.bubbles && !isStateEvent) ? 4 : 2
+ right: parent.right
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
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
function fromModel(role) {
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) ?? ""
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) ?? ""
filename: r.relatedEventCacheBuster, fromModel(Room.Filename) ?? ""
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
+ 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
typeString: r.relatedEventCacheBuster, fromModel(Room.TypeString) ?? ""
url: r.relatedEventCacheBuster, fromModel(Room.Url) ?? ""
- originalWidth: r.relatedEventCacheBuster, fromModel(Room.OriginalWidth) ?? 0
- isOnlyEmoji: r.relatedEventCacheBuster, fromModel(Room.IsOnlyEmoji) ?? false
- isStateEvent: r.relatedEventCacheBuster, fromModel(Room.IsStateEvent) ?? false
+ userColor: r.relatedEventCacheBuster, TimelineManager.userColor(userId, palette.base)
userId: r.relatedEventCacheBuster, fromModel(Room.UserId) ?? ""
userName: r.relatedEventCacheBuster, fromModel(Room.UserName) ?? ""
- thumbnailUrl: r.relatedEventCacheBuster, fromModel(Room.ThumbnailUrl) ?? ""
- 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
+ visible: replyTo
}
// actual message content
MessageDelegate {
- Layout.row: 1
+ id: contentItem
+
Layout.column: 0
Layout.fillWidth: true
Layout.preferredHeight: height
- id: contentItem
-
+ Layout.row: 1
blurhash: r.blurhash
body: r.body
- formattedBody: r.formattedBody
+ callType: r.callType
+ duration: r.duration
+ encryptionError: r.encryptionError
eventId: r.eventId
filename: r.filename
filesize: r.filesize
+ formattedBody: r.formattedBody
+ isOnlyEmoji: r.isOnlyEmoji
+ isReply: false
+ isStateEvent: r.isStateEvent
+ metadataWidth: metadata.width
+ originalWidth: r.originalWidth
proportionalHeight: r.proportionalHeight
+ relatedEventCacheBuster: r.relatedEventCacheBuster
+ roomName: r.roomName
+ roomTopic: r.roomTopic
+ thumbnailUrl: r.thumbnailUrl
type: r.type
typeString: r.typeString ?? ""
url: r.url
- thumbnailUrl: r.thumbnailUrl
- duration: r.duration
- originalWidth: r.originalWidth
- isOnlyEmoji: r.isOnlyEmoji
- isStateEvent: r.isStateEvent
userId: r.userId
userName: r.userName
- roomTopic: r.roomTopic
- roomName: r.roomName
- callType: r.callType
- encryptionError: r.encryptionError
- relatedEventCacheBuster: r.relatedEventCacheBuster
- isReply: false
- metadataWidth: metadata.width
}
-
Row {
id: metadata
- Layout.column: Settings.bubbles? 0 : 1
- Layout.row: Settings.bubbles? 2 : 0
- Layout.rowSpan: Settings.bubbles? 1 : 2
- Layout.bottomMargin: -2
- Layout.topMargin: (contentItem.fitsMetadata && Settings.bubbles)? -height-Layout.bottomMargin : 0
+
+ property int iconSize: Math.floor(fontMetrics.ascent * scaling)
+ property double scaling: Settings.bubbles ? 0.75 : 1
+
Layout.alignment: Qt.AlignTop | Qt.AlignRight
+ Layout.bottomMargin: -2
+ Layout.column: Settings.bubbles ? 0 : 1
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
-
- property double scaling: Settings.bubbles? 0.75 : 1
-
- property int iconSize: Math.floor(fontMetrics.ascent*scaling)
+ visible: !isStateEvent
StatusIndicator {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
- height: parent.iconSize
- width: parent.iconSize
- status: r.status
- eventId: r.eventId
anchors.verticalCenter: ts.verticalCenter
- }
-
- Image {
- visible: isEdited || eventId == room.edit
- Layout.alignment: Qt.AlignRight | Qt.AlignTop
+ eventId: r.eventId
height: parent.iconSize
+ status: r.status
width: parent.iconSize
- sourceSize.width: parent.iconSize * Screen.devicePixelRatio
- sourceSize.height: parent.iconSize * Screen.devicePixelRatio
- source: "image://colorimage/:/icons/icons/ui/edit.svg?" + ((eventId == room.edit) ? Nheko.colors.highlight : Nheko.colors.buttonText)
- ToolTip.visible: editHovered.hovered
+ }
+ Image {
+ Layout.alignment: Qt.AlignRight | Qt.AlignTop
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: qsTr("Edited")
+ ToolTip.visible: editHovered.hovered
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 {
id: editHovered
+
}
-
}
-
ImageButton {
- visible: threadId
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.text: qsTr("Part of a thread")
+ ToolTip.visible: hovered
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
}
-
EncryptionIndicator {
- visible: room.isEncrypted
- encrypted: isEncrypted
- trust: trustlevel
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
+ 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 {
id: ts
+
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredWidth: implicitWidth
- text: timestamp.toLocaleTimeString(Locale.ShortFormat)
- color: Nheko.inactiveColors.text
- ToolTip.visible: ma.hovered
ToolTip.delay: Nheko.tooltipDelay
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 {
id: ma
- }
+ }
}
}
}
}
-
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
- reactions: r.reactions
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 {
id: unreadRow
+
+ color: palette.highlight
+ height: visible ? 3 : 0
+ visible: (r.index > 0 && (room.fullyReadEventId == r.eventId))
+
anchors {
- top: reactionRow.bottom
- topMargin: 5
left: parent.left
right: parent.right
+ top: reactionRow.bottom
+ topMargin: 5
}
- color: Nheko.colors.highlight
-
- visible: (r.index > 0 && (room.fullyReadEventId == r.eventId))
- height: visible ? 3 : 0
-
}
}
diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index c8b22616..bbcf2366 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -8,101 +8,97 @@ import "./device-verification"
import "./emoji"
import "./ui"
import "./voip"
-import Qt.labs.platform 1.1 as Platform
-import QtQuick 2.15
-import QtQuick.Controls 2.5
-import QtQuick.Layouts 1.3
-import QtQuick.Particles 2.15
-import QtQuick.Window 2.13
-import im.nheko 1.0
-import im.nheko.EmojiModel 1.0
+import Qt.labs.platform as Platform
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Particles
+import QtQuick.Window
+import im.nheko
Item {
id: timelineView
+ required property PrivacyScreen privacyScreen
property var room: null
property var roomPreview: null
- property bool showBackButton: false
property bool shouldEffectsRun: false
- required property PrivacyScreen privacyScreen
+ property bool showBackButton: false
+
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.
- Keys.onPressed: {
+ Keys.onPressed: event => {
if (event.text && event.key !== Qt.Key_Enter && event.key !== Qt.Key_Return && !topBar.searchHasFocus) {
TimelineManager.focusMessageInput();
room.input.setText(room.input.text + event.text);
}
}
+ onRoomChanged: if (room != null)
+ room.triggerSpecialEffects()
+ StickerPicker {
+ id: emojiPopup
+
+ emoji: true
+ }
Shortcut {
sequence: StandardKey.Close
+
onActivated: Rooms.resetCurrentRoom()
}
-
Label {
- visible: !room && !TimelineManager.isInitialSync && (!roomPreview || !roomPreview.roomid)
anchors.centerIn: parent
- text: qsTr("No room open")
font.pointSize: 24
- color: Nheko.colors.text
+ text: qsTr("No room open")
+ visible: !room && !TimelineManager.isInitialSync && (!roomPreview || !roomPreview.roomid)
}
-
Spinner {
- visible: TimelineManager.isInitialSync
anchors.centerIn: parent
- foreground: Nheko.colors.mid
- running: TimelineManager.isInitialSync
+ foreground: palette.mid
// height is somewhat arbitrary here... don't set width because width scales w/ height
height: parent.height / 16
- z: 3
opacity: hh.hovered ? 0.3 : 1
+ running: TimelineManager.isInitialSync
+ visible: TimelineManager.isInitialSync
+ z: 3
- Behavior on opacity {
- NumberAnimation { duration: 100; }
+ Behavior on opacity {
+ NumberAnimation {
+ duration: 100
+ }
}
HoverHandler {
id: hh
+
}
}
-
ColumnLayout {
id: timelineLayout
- visible: room != null && !room.isSpace
- enabled: visible
anchors.fill: parent
+ enabled: visible
spacing: 0
+ visible: room != null && !room.isSpace
TopBar {
id: topBar
showBackButton: timelineView.showBackButton
}
-
Rectangle {
Layout.fillWidth: true
+ color: Nheko.theme.separator
height: 1
z: 3
- color: Nheko.theme.separator
}
-
Rectangle {
id: msgView
- Layout.fillWidth: true
Layout.fillHeight: true
- color: Nheko.colors.base
+ Layout.fillWidth: true
+ color: palette.base
ColumnLayout {
anchors.fill: parent
@@ -120,143 +116,121 @@ Item {
target: timelineView
}
-
MessageView {
+ Layout.fillWidth: true
implicitHeight: msgView.height - typingIndicator.height
searchString: topBar.searchString
- Layout.fillWidth: true
}
-
Loader {
- source: CallManager.isOnCall && CallManager.callType != CallType.VOICE ? "voip/VideoCall.qml" : ""
+ source: CallManager.isOnCall && CallManager.callType != Voip.VOICE ? "voip/VideoCall.qml" : ""
+
onLoaded: TimelineManager.setVideoCallItem()
}
-
}
-
TypingIndicator {
id: typingIndicator
+
}
-
}
-
}
-
CallInviteBar {
id: callInviteBar
Layout.fillWidth: true
z: 3
}
-
ActiveCallBar {
Layout.fillWidth: true
z: 3
}
-
Rectangle {
Layout.fillWidth: true
- z: 3
- height: 1
color: Nheko.theme.separator
+ height: 1
+ z: 3
}
-
-
UploadBox {
}
-
MessageInputWarning {
text: qsTr("You are about to notify the whole room")
visible: (room && room.permissions.canPingRoom() && room.input.containsAtRoom)
}
-
MessageInputWarning {
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
}
-
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 : "")
visible: room ? room.input.containsIncompleteCommand : false
- bubbleColor: Nheko.theme.orange
}
-
ReplyPopup {
}
-
MessageInput {
}
-
}
-
ColumnLayout {
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 roomName: room ? room.roomName : (roomPreview ? roomPreview.roomName : "")
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.margins: Nheko.paddingLarge
+ enabled: visible
spacing: Nheko.paddingLarge
+ visible: room != null && room.isSpace || roomPreview != null
Item {
Layout.fillHeight: true
}
-
Avatar {
- url: parent.avatarUrl.replace("mxc://", "image://MxcImage/")
- roomid: parent.roomId
+ Layout.alignment: Qt.AlignHCenter
displayName: parent.roomName
- height: 130
- width: 130
- Layout.alignment: Qt.AlignHCenter
enabled: false
+ height: 130
+ roomid: parent.roomId
+ url: parent.avatarUrl.replace("mxc://", "image://MxcImage/")
+ width: 130
}
-
RowLayout {
- spacing: Nheko.paddingMedium
Layout.alignment: Qt.AlignHCenter
+ spacing: Nheko.paddingMedium
MatrixText {
- text: !roomPreview.isFetched ? qsTr("No preview available") : preview.roomName
font.pixelSize: 24
+ text: !(roomPreview?.isFetched ?? false) ? qsTr("No preview available") : preview.roomName
}
-
ImageButton {
+ ToolTip.text: qsTr("Settings")
+ ToolTip.visible: hovered
+ hoverEnabled: true
image: ":/icons/icons/ui/settings.svg"
visible: !!room
- hoverEnabled: true
- ToolTip.visible: hovered
- ToolTip.text: qsTr("Settings")
+
onClicked: TimelineManager.openRoomSettings(room.roomId)
}
-
}
-
RowLayout {
- visible: !!room
- spacing: Nheko.paddingMedium
Layout.alignment: Qt.AlignHCenter
+ spacing: Nheko.paddingMedium
+ visible: !!room
MatrixText {
text: qsTr("%n member(s)", "", room ? room.roomMemberCount : 0)
}
-
ImageButton {
- image: ":/icons/icons/ui/people.svg"
- hoverEnabled: true
- ToolTip.visible: hovered
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)
}
-
}
-
ScrollView {
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
@@ -264,55 +238,76 @@ Item {
Layout.rightMargin: Nheko.paddingLarge
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
- selectByMouse: true
- color: Nheko.colors.text
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)
- CursorShape {
+ NhekoCursorShape {
anchors.fill: parent
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
-
}
-
}
-
FlatButton {
- visible: roomPreview && !roomPreview.isInvite
Layout.alignment: Qt.AlignHCenter
text: qsTr("join the conversation")
+ visible: roomPreview && !roomPreview.isInvite
+
onClicked: Rooms.joinPreview(roomPreview.roomid)
}
-
FlatButton {
- visible: roomPreview && roomPreview.isInvite
Layout.alignment: Qt.AlignHCenter
text: qsTr("accept invite")
+ visible: roomPreview && roomPreview.isInvite
+
onClicked: Rooms.acceptInvite(roomPreview.roomid)
}
-
FlatButton {
- visible: roomPreview && roomPreview.isInvite
Layout.alignment: Qt.AlignHCenter
text: qsTr("decline invite")
+ visible: roomPreview && roomPreview.isInvite
+
onClicked: Rooms.declineInvite(roomPreview.roomid)
}
-
FlatButton {
- visible: !!room
Layout.alignment: Qt.AlignHCenter
text: qsTr("leave")
+ visible: !!room
+
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 {
id: reasonField
+
property bool showReason: false
Layout.alignment: Qt.AlignHCenter
@@ -322,18 +317,15 @@ Item {
visible: preview.reason !== "" && showReason
TextArea {
- text: TimelineManager.escapeEmoji(preview.reason)
- wrapMode: TextEdit.WordWrap
- textFormat: TextEdit.RichText
- readOnly: true
background: null
- selectByMouse: true
- color: Nheko.colors.text
horizontalAlignment: TextEdit.AlignHCenter
+ readOnly: true
+ selectByMouse: true
+ text: TimelineManager.escapeEmoji(preview.reason)
+ textFormat: TextEdit.RichText
+ wrapMode: TextEdit.WordWrap
}
-
}
-
Button {
id: showReasonButton
@@ -341,76 +333,95 @@ Item {
//Layout.fillWidth: true
Layout.leftMargin: Nheko.paddingLarge
Layout.rightMargin: Nheko.paddingLarge
-
- visible: preview.reason !== ""
text: reasonField.showReason ? qsTr("Hide invite reason") : qsTr("Show invite reason")
+ visible: roomPreview && roomPreview.isInvite
+
onClicked: {
reasonField.showReason = !reasonField.showReason;
}
}
-
Item {
- visible: room != null
Layout.preferredHeight: Math.ceil(fontMetrics.lineSpacing * 2)
+ visible: room != null
}
-
Item {
Layout.fillHeight: true
}
-
}
-
ImageButton {
id: backToRoomsButton
- anchors.top: parent.top
+ ToolTip.text: qsTr("Back to room list")
+ ToolTip.visible: hovered
anchors.left: parent.left
anchors.margins: Nheko.paddingMedium
- width: Nheko.avatarSize
- height: Nheko.avatarSize
- visible: (room == null || room.isSpace) && showBackButton
+ anchors.top: parent.top
enabled: visible
+ height: Nheko.avatarSize
image: ":/icons/icons/ui/angle-arrow-left.svg"
- ToolTip.visible: hovered
- ToolTip.text: qsTr("Back to room list")
+ visible: (room == null || room.isSpace) && showBackButton
+ width: Nheko.avatarSize
+
onClicked: Rooms.resetCurrentRoom()
}
-
TimelineEffects {
id: timelineEffects
anchors.fill: parent
+ shouldEffectsRun: timelineView.shouldEffectsRun
}
-
NhekoDropArea {
anchors.fill: parent
roomid: room ? room.roomId : ""
}
-
Timer {
id: effectsTimer
- onTriggered: shouldEffectsRun = false;
+
interval: timelineEffects.maxLifespan
repeat: false
running: false
- }
+ onTriggered: shouldEffectsRun = false
+ }
Connections {
+ function onConfetti() {
+ if (!Settings.fancyEffects)
+ return;
+ shouldEffectsRun = true;
+ timelineEffects.pulseConfetti();
+ room.markSpecialEffectsDone();
+ }
+ function onConfettiDone() {
+ if (!Settings.fancyEffects)
+ return;
+ effectsTimer.restart();
+ }
function onOpenReadReceiptsDialog(rr) {
var dialog = readReceiptsDialog.createObject(timelineRoot, {
- "readReceipts": rr,
- "room": room
- });
+ "readReceipts": rr,
+ "room": room
+ });
dialog.show();
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) {
- var component = Qt.createComponent("qrc:/qml/dialogs/RawMessageDialog.qml")
+ var component = Qt.createComponent("qrc:/resources/qml/dialogs/RawMessageDialog.qml");
if (component.status == Component.Ready) {
var dialog = component.createObject(timelineRoot, {
- "rawMessage": rawMessage
- });
+ "rawMessage": rawMessage
+ });
dialog.show();
timelineRoot.destroyOnClose(dialog);
} 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
}
-
}
diff --git a/resources/qml/ToggleButton.qml b/resources/qml/ToggleButton.qml
index 6b43bec5..f3bd5cce 100644
--- a/resources/qml/ToggleButton.qml
+++ b/resources/qml/ToggleButton.qml
@@ -11,17 +11,44 @@ Switch {
id: toggleButton
implicitWidth: indicatorItem.width
-
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: [
State {
name: "off"
PropertyChanges {
- target: track
border.color: "#767676"
+ target: track
}
-
PropertyChanges {
target: handle
x: 0
@@ -31,10 +58,9 @@ Switch {
name: "on"
PropertyChanges {
+ border.color: palette.highlight
target: track
- border.color: Nheko.colors.highlight
}
-
PropertyChanges {
target: handle
x: indicatorItem.width - handle.width
@@ -43,55 +69,22 @@ Switch {
]
transitions: [
Transition {
- to: "off"
reversible: true
+ to: "off"
ParallelAnimation {
NumberAnimation {
- target: handle
- property: "x"
duration: 200
easing.type: Easing.InOutQuad
+ property: "x"
+ target: handle
}
-
ColorAnimation {
- target: track
- properties: "color,border.color"
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"
- }
-
- }
-
}
diff --git a/resources/qml/TopBar.qml b/resources/qml/TopBar.qml
index f23645a7..699595e6 100644
--- a/resources/qml/TopBar.qml
+++ b/resources/qml/TopBar.qml
@@ -8,212 +8,142 @@ import QtQuick.Controls 2.15
import QtQuick.Layouts 1.2
import QtQuick.Window 2.15
import im.nheko 1.0
-
import "./delegates"
Pane {
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 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 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 string searchString: ""
-
- // HACK: https://bugreports.qt.io/browse/QTBUG-83972, qtwayland cannot auto hide menu
- Connections {
- function onHideMenu() {
- roomOptionsMenu.close()
- }
- target: MainWindow
- }
-
- onRoomIdChanged: {
- searchString = "";
- searchButton.searchActive = false;
- searchField.text = ""
- }
-
- Shortcut {
- sequence: StandardKey.Find
- onActivated: searchButton.searchActive = !searchButton.searchActive
- }
+ property bool showBackButton: false
+ property int trustlevel: room ? room.trustlevel : Crypto.Unverified
Layout.fillWidth: true
implicitHeight: topLayout.height + Nheko.paddingMedium * 2
+ padding: 0
z: 3
- padding: 0
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 {
GridLayout {
id: topLayout
anchors.left: parent.left
- anchors.right: parent.right
anchors.margins: Nheko.paddingMedium
+ anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
columnSpacing: Nheko.paddingSmall
rowSpacing: Nheko.paddingSmall
-
Avatar {
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 communityId: (Settings.groupView && room && room.parentSpace && room.parentSpace.roomid) || ""
property string communityName: (Settings.groupView && room && room.parentSpace && room.parentSpace.roomName) || ""
+ Layout.alignment: Qt.AlignRight
Layout.column: 1
Layout.row: 0
- Layout.alignment: Qt.AlignRight
- width: fontMetrics.lineSpacing
- height: fontMetrics.lineSpacing
- url: avatarUrl.replace("mxc://", "image://MxcImage/")
- roomid: communityId
displayName: communityName
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 {
id: communityLabel
- visible: communityAvatar.visible
Layout.column: 2
- Layout.row: 0
Layout.fillWidth: true
- color: Nheko.colors.text
- text: qsTr("In %1").arg(communityAvatar.displayName)
- maximumLineCount: 1
+ Layout.row: 0
+ color: palette.text
elide: Text.ElideRight
+ maximumLineCount: 1
+ text: qsTr("In %1").arg(communityAvatar.displayName)
textFormat: Text.RichText
+ visible: communityAvatar.visible
}
-
ImageButton {
id: backToRoomsButton
- Layout.column: 0
- Layout.row: 1
- Layout.rowSpan: 2
Layout.alignment: Qt.AlignVCenter
+ Layout.column: 0
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
- visible: showBackButton
- image: ":/icons/icons/ui/angle-arrow-left.svg"
- ToolTip.visible: hovered
+ Layout.row: 1
+ Layout.rowSpan: 2
ToolTip.text: qsTr("Back to room list")
+ ToolTip.visible: hovered
+ image: ":/icons/icons/ui/angle-arrow-left.svg"
+ visible: showBackButton
+
onClicked: Rooms.resetCurrentRoom()
}
-
Avatar {
+ Layout.alignment: Qt.AlignVCenter
Layout.column: 1
Layout.row: 1
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
enabled: false
+ height: Nheko.avatarSize
+ roomid: roomId
+ url: avatarUrl.replace("mxc://", "image://MxcImage/")
+ userid: isDirect ? directChatOtherUserId : ""
+ width: Nheko.avatarSize
}
-
Label {
- Layout.fillWidth: true
Layout.column: 2
+ Layout.fillWidth: true
Layout.row: 1
- color: Nheko.colors.text
- font.pointSize: fontMetrics.font.pointSize * 1.1
- font.bold: true
- text: roomName
- maximumLineCount: 1
+ color: palette.text
elide: Text.ElideRight
+ font.bold: true
+ font.pointSize: fontMetrics.font.pointSize * 1.1
+ maximumLineCount: 1
+ text: roomName
textFormat: Text.RichText
}
-
MatrixText {
id: roomTopicC
- Layout.fillWidth: true
+
Layout.column: 2
- Layout.row: 2
+ Layout.fillWidth: true
Layout.maximumHeight: fontMetrics.lineSpacing * 2 // show 2 lines
- selectByMouse: false
- enabled: false
+ Layout.row: 2
clip: true
+ enabled: false
+ selectByMouse: false
text: roomTopic
}
-
ImageButton {
id: pinButton
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.column: 3
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
- image: pinsShown ? ":/icons/icons/ui/pin.svg" : ":/icons/icons/ui/pin-off.svg"
- ToolTip.visible: hovered
+ Layout.row: 1
+ Layout.rowSpan: 2
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: {
var ps = Settings.hiddenPins;
if (pinsShown) {
@@ -226,254 +156,280 @@ Pane {
}
Settings.hiddenPins = ps;
}
-
}
-
AbstractButton {
Layout.column: 4
- Layout.row: 1
- Layout.rowSpan: 2
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
+ Layout.row: 1
+ Layout.rowSpan: 2
+ background: null
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.text: {
if (!isEncrypted)
- return qsTr("Show room members.");
-
+ return qsTr("Show room members.");
switch (trustlevel) {
- case Crypto.Verified:
+ case Crypto.Verified:
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.");
- default:
+ default:
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)
}
-
ImageButton {
id: searchButton
property bool searchActive: false
- visible: !!room
- Layout.column: 5
- Layout.row: 1
- Layout.rowSpan: 2
Layout.alignment: Qt.AlignVCenter
+ Layout.column: 5
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
- image: ":/icons/icons/ui/search.svg"
- ToolTip.visible: hovered
+ Layout.row: 1
+ Layout.rowSpan: 2
ToolTip.text: qsTr("Search this room")
- onClicked: searchActive = !searchActive
+ ToolTip.visible: hovered
+ image: ":/icons/icons/ui/search.svg"
+ visible: !!room
+ onClicked: searchActive = !searchActive
onSearchActiveChanged: {
if (searchActive) {
searchField.forceActiveFocus();
- }
- else {
+ } else {
searchField.clear();
topBar.searchString = "";
}
}
}
-
ImageButton {
id: roomOptionsButton
- visible: !!room
- Layout.column: 6
- Layout.row: 1
- Layout.rowSpan: 2
Layout.alignment: Qt.AlignVCenter
+ Layout.column: 6
Layout.preferredHeight: Nheko.avatarSize - Nheko.paddingMedium
Layout.preferredWidth: Nheko.avatarSize - Nheko.paddingMedium
- image: ":/icons/icons/ui/options.svg"
- ToolTip.visible: hovered
+ Layout.row: 1
+ Layout.rowSpan: 2
ToolTip.text: qsTr("Room options")
+ ToolTip.visible: hovered
+ image: ":/icons/icons/ui/options.svg"
+ visible: !!room
+
onClicked: roomOptionsMenu.open(roomOptionsButton)
Platform.Menu {
id: roomOptionsMenu
Platform.MenuItem {
- visible: room ? room.permissions.canInvite() : false
text: qsTr("Invite users")
+ visible: room ? room.permissions.canInvite() : false
+
onTriggered: TimelineManager.openInviteUsers(roomId)
}
-
Platform.MenuItem {
text: qsTr("Members")
+
onTriggered: TimelineManager.openRoomMembers(room)
}
-
Platform.MenuItem {
text: qsTr("Leave room")
+
onTriggered: TimelineManager.openLeaveRoomDialog(roomId)
}
-
Platform.MenuItem {
text: qsTr("Settings")
+
onTriggered: TimelineManager.openRoomSettings(roomId)
}
-
}
-
}
-
ScrollView {
id: pinnedMessages
- Layout.row: 3
Layout.column: 2
Layout.columnSpan: 4
-
Layout.fillWidth: true
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 4)
-
- visible: !!room && room.pinnedMessages.length > 0 && !Settings.hiddenPins.includes(roomId)
- clip: true
-
- palette: Nheko.colors
+ Layout.row: 3
ScrollBar.horizontal.visible: false
+ clip: true
+ visible: !!room && room.pinnedMessages.length > 0 && !Settings.hiddenPins.includes(roomId)
ListView {
-
- spacing: Nheko.paddingSmall
model: room ? room.pinnedMessages : undefined
+ spacing: Nheko.paddingSmall
+
delegate: RowLayout {
required property string modelData
- width: ListView.view.width
height: implicitHeight
+ width: ListView.view.width
Reply {
id: reply
+
property var e: room ? room.getDump(modelData, "pins") : {}
- Connections {
- function onPinnedMessagesChanged() { reply.e = room.getDump(modelData, "pins") }
- target: room
- }
+
Layout.fillWidth: true
Layout.preferredHeight: height
-
- userColor: TimelineManager.userColor(e.userId, Nheko.colors.window)
blurhash: e.blurhash ?? ""
body: e.body ?? ""
- formattedBody: e.formattedBody ?? ""
+ encryptionError: e.encryptionError ?? 0
eventId: e.eventId ?? ""
filename: e.filename ?? ""
filesize: e.filesize ?? ""
+ formattedBody: e.formattedBody ?? ""
+ isOnlyEmoji: e.isOnlyEmoji ?? false
+ keepFullText: true
+ originalWidth: e.originalWidth ?? 0
proportionalHeight: e.proportionalHeight ?? 1
type: e.type ?? MtxEvent.UnknownMessage
typeString: e.typeString ?? ""
url: e.url ?? ""
- originalWidth: e.originalWidth ?? 0
- isOnlyEmoji: e.isOnlyEmoji ?? false
+ userColor: TimelineManager.userColor(e.userId, palette.window)
userId: e.userId ?? ""
userName: e.userName ?? ""
- encryptionError: e.encryptionError ?? ""
- keepFullText: true
- }
+ Connections {
+ function onPinnedMessagesChanged() {
+ reply.e = room.getDump(modelData, "pins");
+ }
+
+ target: room
+ }
+ }
ImageButton {
id: deletePinButton
+ Layout.alignment: Qt.AlignTop | Qt.AlignLeft
Layout.preferredHeight: 16
Layout.preferredWidth: 16
- Layout.alignment: Qt.AlignTop | Qt.AlignLeft
- visible: room.permissions.canChange(MtxEvent.PinnedEvents)
-
+ ToolTip.text: qsTr("Unpin")
+ ToolTip.visible: hovered
hoverEnabled: true
image: ":/icons/icons/ui/dismiss.svg"
- ToolTip.visible: hovered
- ToolTip.text: qsTr("Unpin")
+ visible: room.permissions.canChange(MtxEvent.PinnedEvents)
onClicked: room.unpin(modelData)
}
}
-
-
- ScrollHelper {
- flickable: parent
- anchors.fill: parent
- enabled: !Settings.mobileMode
- }
}
}
-
ScrollView {
id: widgets
- Layout.row: 4
Layout.column: 2
Layout.columnSpan: 4
-
Layout.fillWidth: true
Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 1.5)
-
- visible: !!room && room.widgetLinks.length > 0 && !Settings.hiddenWidgets.includes(roomId)
- clip: true
-
- palette: Nheko.colors
+ Layout.row: 4
ScrollBar.horizontal.visible: false
+ clip: true
+ visible: !!room && room.widgetLinks.length > 0 && !Settings.hiddenWidgets.includes(roomId)
ListView {
-
- spacing: Nheko.paddingSmall
model: room ? room.widgetLinks : undefined
+ spacing: Nheko.paddingSmall
+
delegate: MatrixText {
required property var modelData
- color: Nheko.colors.text
+ color: palette.text
text: modelData
}
-
-
- ScrollHelper {
- flickable: parent
- anchors.fill: parent
- enabled: !Settings.mobileMode
- }
}
}
-
MatrixTextField {
id: searchField
- visible: searchButton.searchActive
- enabled: visible
- hasClear: true
- Layout.row: 5
Layout.column: 2
Layout.columnSpan: 4
-
Layout.fillWidth: true
-
+ Layout.row: 5
+ enabled: visible
+ hasClear: true
placeholderText: qsTr("Enter search query")
+ visible: searchButton.searchActive
+
onAccepted: topBar.searchString = text
}
}
-
- CursorShape {
- anchors.fill: parent
+ NhekoCursorShape {
anchors.bottomMargin: (pinnedMessages.visible ? pinnedMessages.height : 0) + (widgets.visible ? widgets.height : 0)
+ anchors.fill: parent
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
+ }
}
diff --git a/resources/qml/TypingIndicator.qml b/resources/qml/TypingIndicator.qml
index 027ae5b6..b6c502d8 100644
--- a/resources/qml/TypingIndicator.qml
+++ b/resources/qml/TypingIndicator.qml
@@ -8,30 +8,28 @@ import QtQuick.Layouts 1.2
import im.nheko 1.0
Item {
- implicitHeight: Math.max(fontMetrics.height * 1.2, typingDisplay.height)
Layout.fillWidth: true
+ implicitHeight: Math.max(fontMetrics.height * 1.2, typingDisplay.height)
Rectangle {
id: typingRect
- visible: (room && room.typingUsers.length > 0)
- color: Nheko.colors.base
anchors.fill: parent
+ color: palette.base
+ visible: (room && room.typingUsers.length > 0)
z: 3
Label {
id: typingDisplay
+ anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.leftMargin: 10
anchors.right: parent.right
anchors.rightMargin: 10
- anchors.bottom: parent.bottom
- color: Nheko.colors.text
- text: room ? room.formatTypingUsers(room.typingUsers, Nheko.colors.base) : ""
+ color: palette.text
+ text: room ? room.formatTypingUsers(room.typingUsers, palette.base) : ""
textFormat: Text.RichText
}
-
}
-
}
diff --git a/resources/qml/UploadBox.qml b/resources/qml/UploadBox.qml
index adb8c12b..990fa422 100644
--- a/resources/qml/UploadBox.qml
+++ b/resources/qml/UploadBox.qml
@@ -4,7 +4,6 @@
import "./components"
import "./ui"
-
import QtQuick 2.9
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
@@ -12,79 +11,85 @@ import im.nheko 1.0
Page {
id: uploadPopup
- visible: room && room.input.uploads.length > 0
- Layout.preferredHeight: 200
- clip: true
Layout.fillWidth: true
-
+ Layout.preferredHeight: 200
+ clip: true
padding: Nheko.paddingMedium
+ visible: room && room.input.uploads.length > 0
+ background: Rectangle {
+ color: palette.base
+ }
contentItem: ListView {
id: uploadsList
+
anchors.horizontalCenter: parent.horizontalCenter
boundsBehavior: Flickable.StopAtBounds
+ model: room ? room.input.uploads : undefined
+ orientation: ListView.Horizontal
+ spacing: Nheko.paddingMedium
+ width: Math.min(contentWidth, parent.availableWidth)
ScrollBar.horizontal: ScrollBar {
id: scr
+
}
-
- orientation: ListView.Horizontal
- width: Math.min(contentWidth, parent.availableWidth)
- model: room ? room.input.uploads : undefined
- spacing: Nheko.paddingMedium
-
delegate: Pane {
+ id: pane
+
+ height: uploadPopup.availableHeight - buttons.height - (scr.visible ? scr.height : 0)
padding: Nheko.paddingSmall
- height: uploadPopup.availableHeight - buttons.height - (scr.visible? scr.height : 0)
width: uploadPopup.availableHeight - buttons.height
background: Rectangle {
- color: Nheko.colors.window
+ color: palette.window
radius: Nheko.paddingMedium
}
contentItem: ColumnLayout {
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.fillWidth: true
-
- sourceSize.height: parent.availableHeight - namefield.height
- sourceSize.width: parent.availableWidth
fillMode: Image.PreserveAspectFit
- smooth: true
mipmap: true
-
- 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";
- }
- source: (modelData.thumbnail != "") ? modelData.thumbnail : ("image://colorimage/:/icons/icons/ui/"+typeStr+".svg?" + Nheko.colors.buttonText)
+ smooth: true
+ source: (modelData.thumbnail != "") ? modelData.thumbnail : ("image://colorimage/:/icons/icons/ui/" + typeStr + ".svg?" + palette.buttonText)
+ sourceSize.height: pane.availableHeight - namefield.height
+ sourceSize.width: pane.availableWidth
}
MatrixTextField {
id: namefield
+
Layout.fillWidth: true
text: modelData.filename
+
onTextEdited: modelData.filename = text
}
}
}
}
-
footer: DialogButtonBox {
id: buttons
standardButtons: DialogButtonBox.Cancel
- Button {
- text: qsTr("Upload %n file(s)", "", (room ? room.input.uploads.length : 0))
- DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
- }
+
onAccepted: room.input.acceptUploads()
onRejected: room.input.declineUploads()
- }
- background: Rectangle {
- color: Nheko.colors.base
+ Button {
+ DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
+ text: qsTr("Upload %n file(s)", "", (room ? room.input.uploads.length : 0))
+ }
}
}
diff --git a/resources/qml/components/AdaptiveLayout.qml b/resources/qml/components/AdaptiveLayout.qml
index 9fc27055..eea74006 100644
--- a/resources/qml/components/AdaptiveLayout.qml
+++ b/resources/qml/components/AdaptiveLayout.qml
@@ -87,7 +87,7 @@ Container {
x: parent.preferredWidth
z: 3
- CursorShape {
+ NhekoCursorShape {
height: parent.height
width: container.splitterGrabMargin * 2
x: -container.splitterGrabMargin
diff --git a/resources/qml/components/AvatarListTile.qml b/resources/qml/components/AvatarListTile.qml
index 02c92a09..dad20e52 100644
--- a/resources/qml/components/AvatarListTile.qml
+++ b/resources/qml/components/AvatarListTile.qml
@@ -11,11 +11,11 @@ import im.nheko 1.0
Rectangle {
id: tile
- property color background: 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
+ property color background: palette.window
+ property color importantText: palette.text
+ property color unimportantText: palette.buttonText
+ property color bubbleBackground: palette.highlight
+ property color bubbleText: palette.highlightedText
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
required property string avatarUrl
required property string title
@@ -37,11 +37,11 @@ Rectangle {
PropertyChanges {
target: tile
- background: Nheko.colors.dark
- importantText: Nheko.colors.brightText
- unimportantText: Nheko.colors.brightText
- bubbleBackground: Nheko.colors.highlight
- bubbleText: Nheko.colors.highlightedText
+ background: palette.dark
+ importantText: palette.brightText
+ unimportantText: palette.brightText
+ bubbleBackground: palette.highlight
+ bubbleText: palette.highlightedText
}
},
@@ -51,11 +51,11 @@ Rectangle {
PropertyChanges {
target: tile
- background: Nheko.colors.highlight
- importantText: Nheko.colors.highlightedText
- unimportantText: Nheko.colors.highlightedText
- bubbleBackground: Nheko.colors.highlightedText
- bubbleText: Nheko.colors.highlight
+ background: palette.highlight
+ importantText: palette.highlightedText
+ unimportantText: palette.highlightedText
+ bubbleBackground: palette.highlightedText
+ bubbleText: palette.highlight
}
}
diff --git a/resources/qml/components/FlatButton.qml b/resources/qml/components/FlatButton.qml
index ec4b306a..0636bc04 100644
--- a/resources/qml/components/FlatButton.qml
+++ b/resources/qml/components/FlatButton.qml
@@ -2,11 +2,11 @@
//
// SPDX-License-Identifier: GPL-3.0-or-later
-import QtGraphicalEffects 1.12
-import QtQuick 2.9
-import QtQuick.Controls 2.5
-import QtQuick.Layouts 1.2
-import im.nheko 1.0
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Effects
+import im.nheko
// FIXME(Nico): Don't use hardcoded colors.
Button {
@@ -18,14 +18,13 @@ Button {
property string iconImage: ""
- DropShadow {
+ MultiEffect {
anchors.fill: control.background
- horizontalOffset: 3
- verticalOffset: 3
- radius: 8
- samples: 17
- cached: true
- color: "#80000000"
+ shadowHorizontalOffset: 3
+ shadowVerticalOffset: 3
+ shadowBlur: 8
+ shadowEnabled: true
+ shadowColor: "#80000000"
source: control.background
}
@@ -48,7 +47,7 @@ Button {
font.capitalization: Font.AllUppercase
font.pointSize: Math.ceil(fontMetrics.font.pointSize * 1.5)
//font.capitalization: Font.AllUppercase
- color: Nheko.colors.light
+ color: palette.light
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
@@ -59,7 +58,7 @@ Button {
//height: control.contentItem.implicitHeight * 2
//width: control.contentItem.implicitWidth * 2
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))
}
}
diff --git a/resources/qml/components/MainWindowDialog.qml b/resources/qml/components/MainWindowDialog.qml
index 1b063e0f..10c07aae 100644
--- a/resources/qml/components/MainWindowDialog.qml
+++ b/resources/qml/components/MainWindowDialog.qml
@@ -32,7 +32,7 @@ Dialog {
]
background: Rectangle {
- color: Nheko.colors.window
+ color: palette.window
border.color: Nheko.theme.separator
border.width: 1
radius: Nheko.paddingSmall
diff --git a/resources/qml/components/NhekoTabButton.qml b/resources/qml/components/NhekoTabButton.qml
index 13549068..796177e8 100644
--- a/resources/qml/components/NhekoTabButton.qml
+++ b/resources/qml/components/NhekoTabButton.qml
@@ -13,15 +13,15 @@ TabButton {
text: control.text
font: control.font
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
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Rectangle {
- border.color: control.down ? Nheko.colors.highlight : Nheko.theme.separator
- color: control.checked ? Nheko.colors.highlight : Nheko.colors.base
+ border.color: control.down ? palette.highlight : Nheko.theme.separator
+ color: control.checked ? palette.highlight : palette.base
border.width: 1
radius: 2
}
diff --git a/resources/qml/components/NotificationBubble.qml b/resources/qml/components/NotificationBubble.qml
index f0a526d0..d4838e92 100644
--- a/resources/qml/components/NotificationBubble.qml
+++ b/resources/qml/components/NotificationBubble.qml
@@ -15,6 +15,7 @@ Rectangle {
required property color bubbleTextColor
property bool mayBeVisible: true
property alias font: notificationBubbleText.font
+ baselineOffset: notificationBubbleText.baseline - bubbleRoot.top
visible: mayBeVisible && notificationCount > 0
implicitHeight: notificationBubbleText.height + Nheko.paddingMedium
diff --git a/resources/qml/components/ReorderableListview.qml b/resources/qml/components/ReorderableListview.qml
index 137e92f8..1e8ab7b0 100644
--- a/resources/qml/components/ReorderableListview.qml
+++ b/resources/qml/components/ReorderableListview.qml
@@ -47,9 +47,9 @@ Item {
width: dragArea.width; height: actualDelegate.implicitHeight + 4
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 } }
radius: 2
@@ -105,10 +105,6 @@ Item {
clip: true
anchors { fill: parent; margins: 2 }
- ScrollHelper {
- flickable: parent
- anchors.fill: parent
- }
model: visualModel
diff --git a/resources/qml/components/TextButton.qml b/resources/qml/components/TextButton.qml
index a48aee2b..b6153f22 100644
--- a/resources/qml/components/TextButton.qml
+++ b/resources/qml/components/TextButton.qml
@@ -11,8 +11,8 @@ AbstractButton {
id: button
property alias cursor: mouseArea.cursorShape
- property color highlightColor: Nheko.colors.highlight
- property color buttonTextColor: Nheko.colors.buttonText
+ property color highlightColor: palette.highlight
+ property color buttonTextColor: palette.buttonText
focusPolicy: Qt.NoFocus
width: buttonText.implicitWidth
@@ -32,7 +32,7 @@ AbstractButton {
horizontalAlignment: Text.AlignHCenter
}
- CursorShape {
+ NhekoCursorShape {
id: mouseArea
anchors.fill: parent
diff --git a/resources/qml/components/UserListRow.qml b/resources/qml/components/UserListRow.qml
index 316baab0..2047f700 100644
--- a/resources/qml/components/UserListRow.qml
+++ b/resources/qml/components/UserListRow.qml
@@ -36,7 +36,7 @@ ItemDelegate {
Label {
Layout.fillWidth: true
text: displayName
- color: TimelineManager.userColor(userid, Nheko.colors.window)
+ color: TimelineManager.userColor(userid, palette.window)
font.pointSize: fontMetrics.font.pointSize
}
@@ -44,7 +44,7 @@ ItemDelegate {
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
text: userid
- color: Nheko.colors.buttonText
+ color: palette.buttonText
font.pointSize: fontMetrics.font.pointSize * 0.9
}
}
diff --git a/resources/qml/delegates/Encrypted.qml b/resources/qml/delegates/Encrypted.qml
index 74e1cb0d..fdfe958e 100644
--- a/resources/qml/delegates/Encrypted.qml
+++ b/resources/qml/delegates/Encrypted.qml
@@ -18,7 +18,7 @@ Rectangle {
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
height: contents.implicitHeight + Nheko.paddingMedium * 2
- color: Nheko.colors.alternateBase
+ color: palette.alternateBase
RowLayout {
id: contents
@@ -58,12 +58,11 @@ Rectangle {
return qsTr("Unknown decryption error");
}
}
- color: Nheko.colors.text
+ color: palette.text
width: parent.width
}
Button {
- palette: Nheko.colors
visible: encryptionError == Olm.MissingSession || encryptionError == Olm.MissingSessionIndex
text: qsTr("Request key")
onClicked: room.requestKeyForEvent(eventId)
diff --git a/resources/qml/delegates/EncryptionEnabled.qml b/resources/qml/delegates/EncryptionEnabled.qml
index e38be4b0..0e2b7fc0 100644
--- a/resources/qml/delegates/EncryptionEnabled.qml
+++ b/resources/qml/delegates/EncryptionEnabled.qml
@@ -4,7 +4,6 @@
import ".."
import QtQuick 2.15
-import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import im.nheko 1.0
@@ -15,9 +14,8 @@ Rectangle {
radius: fontMetrics.lineSpacing / 2 + Nheko.paddingMedium
width: parent.width ? Math.min(parent.width, 700) : 0
- anchors.horizontalCenter: parent.horizontalCenter
height: contents.implicitHeight + Nheko.paddingMedium * 2
- color: Nheko.colors.alternateBase
+ color: palette.alternateBase
border.color: Nheko.theme.green
border.width: 2
@@ -31,8 +29,8 @@ Rectangle {
Image {
source: "image://colorimage/:/icons/icons/ui/shield-filled-checkmark.svg?" + Nheko.theme.green
Layout.alignment: Qt.AlignVCenter
- width: 24
- height: width
+ Layout.preferredWidth: 24
+ Layout.preferredHeight: 24
}
Column {
@@ -43,13 +41,13 @@ Rectangle {
text: qsTr("%1 enabled end-to-end encryption").arg(r.username)
font.bold: true
font.pointSize: 14
- color: Nheko.colors.text
+ color: palette.text
width: parent.width
}
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.")
- color: Nheko.colors.text
+ color: palette.text
width: parent.width
}
diff --git a/resources/qml/delegates/FileMessage.qml b/resources/qml/delegates/FileMessage.qml
index b3c44af2..82b82c1b 100644
--- a/resources/qml/delegates/FileMessage.qml
+++ b/resources/qml/delegates/FileMessage.qml
@@ -11,14 +11,13 @@ Item {
required property string filename
required property string filesize
- height: row.height + (Settings.bubbles? 16: 24)
- width: parent.width
- implicitWidth: row.implicitWidth+metadataWidth
+ height: rowa.height + (Settings.bubbles? 16: 24)
+ implicitWidth: rowa.implicitWidth + metadataWidth
property int metadataWidth
property bool fitsMetadata: true
RowLayout {
- id: row
+ id: rowa
anchors.centerIn: parent
width: parent.width - (Settings.bubbles? 16 : 24)
@@ -27,7 +26,7 @@ Item {
Rectangle {
id: button
- color: Nheko.colors.light
+ color: palette.light
radius: 22
height: 44
width: 44
@@ -50,7 +49,7 @@ Item {
gesturePolicy: TapHandler.ReleaseWithinBounds
}
- CursorShape {
+ NhekoCursorShape {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
@@ -67,7 +66,7 @@ Item {
text: filename
textFormat: Text.PlainText
elide: Text.ElideRight
- color: Nheko.colors.text
+ color: palette.text
}
Text {
@@ -77,7 +76,7 @@ Item {
text: filesize
textFormat: Text.PlainText
elide: Text.ElideRight
- color: Nheko.colors.text
+ color: palette.text
}
}
@@ -85,7 +84,7 @@ Item {
}
Rectangle {
- color: Nheko.colors.alternateBase
+ color: palette.alternateBase
z: -1
radius: 10
anchors.fill: parent
diff --git a/resources/qml/delegates/ImageMessage.qml b/resources/qml/delegates/ImageMessage.qml
index bed4b659..20d727c3 100644
--- a/resources/qml/delegates/ImageMessage.qml
+++ b/resources/qml/delegates/ImageMessage.qml
@@ -22,7 +22,7 @@ AbstractButton {
property int tempWidth: originalWidth < 1? 400: originalWidth
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
hoverEnabled: true
@@ -106,14 +106,14 @@ AbstractButton {
]
property int metadataWidth
- property bool fitsMetadata: (parent.width - width) > metadataWidth+4
+ property bool fitsMetadata: parent != null ? (parent.width - width) > metadataWidth+4 : false
Image {
id: img
visible: !mxcimage.loaded
anchors.fill: parent
- source: url.replace("mxc://", "image://MxcImage/") + "?scale"
+ source: url != "" ? (url.replace("mxc://", "image://MxcImage/") + "?scale") : ""
asynchronous: true
fillMode: Image.PreserveAspectFit
smooth: true
@@ -137,7 +137,7 @@ AbstractButton {
id: blurhash_
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
fillMode: Image.PreserveAspectFit
sourceSize.width: parent.width * Screen.devicePixelRatio
@@ -158,7 +158,7 @@ AbstractButton {
width: parent.width
implicitHeight: imgcaption.implicitHeight
anchors.bottom: overlay.bottom
- color: Nheko.colors.window
+ color: palette.window
opacity: 0.75
}
@@ -171,7 +171,7 @@ AbstractButton {
verticalAlignment: Text.AlignVCenter
// See this MSC: https://github.com/matrix-org/matrix-doc/pull/2530
text: filename ? filename : body
- color: Nheko.colors.text
+ color: palette.text
}
}
diff --git a/resources/qml/delegates/MessageDelegate.qml b/resources/qml/delegates/MessageDelegate.qml
index c0bcec0d..68f65062 100644
--- a/resources/qml/delegates/MessageDelegate.qml
+++ b/resources/qml/delegates/MessageDelegate.qml
@@ -13,7 +13,7 @@ Item {
required property bool isReply
property bool keepFullText: !isReply
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 int type
required property string typeString
@@ -39,6 +39,8 @@ Item {
property bool fitsMetadata: (chooser.child && chooser.child.fitsMetadata) ? chooser.child.fitsMetadata : false
property int metadataWidth
+ implicitWidth: chooser.child?.implicitWidth
+
height: chooser.child ? chooser.child.height : Nheko.paddingLarge
DelegateChooser {
@@ -48,7 +50,7 @@ Item {
roleValue: type
//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 {
roleValue: MtxEvent.UnknownEvent
@@ -78,7 +80,6 @@ Item {
}
Button {
- palette: Nheko.colors
Layout.alignment: Qt.AlignHCenter
text: qsTr("Go to replacement room")
onClicked: room.joinReplacementRoom(eventId)
@@ -149,7 +150,7 @@ Item {
NoticeMessage {
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
isOnlyEmoji: d.isOnlyEmoji
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 {
roleValue: MtxEvent.Name
@@ -603,7 +618,7 @@ Item {
roleValue: MtxEvent.Member
ColumnLayout {
- width: parent.width
+ width: parent?.width ?? 100
NoticeMessage {
body: formatted
@@ -617,7 +632,6 @@ Item {
Button {
visible: d.relatedEventCacheBuster, room.showAcceptKnockButton(d.eventId)
- palette: Nheko.colors
Layout.alignment: Qt.AlignHCenter
text: qsTr("Allow them in")
onClicked: room.acceptKnock(eventId)
diff --git a/resources/qml/delegates/NoticeMessage.qml b/resources/qml/delegates/NoticeMessage.qml
index d62afb96..88efe7b7 100644
--- a/resources/qml/delegates/NoticeMessage.qml
+++ b/resources/qml/delegates/NoticeMessage.qml
@@ -9,7 +9,7 @@ import im.nheko 1.0
TextMessage {
property bool isStateEvent
font.italic: true
- color: Nheko.colors.buttonText
+ color: palette.buttonText
font.pointSize: isStateEvent? 0.8*Settings.fontSize : Settings.fontSize
horizontalAlignment: isStateEvent? Text.AlignHCenter : undefined
}
diff --git a/resources/qml/delegates/Pill.qml b/resources/qml/delegates/Pill.qml
index b60781cb..3f981d4d 100644
--- a/resources/qml/delegates/Pill.qml
+++ b/resources/qml/delegates/Pill.qml
@@ -8,14 +8,14 @@ import im.nheko 1.0
Label {
property bool isStateEvent
- color: Nheko.colors.text
+ color: palette.text
horizontalAlignment: Text.AlignHCenter
height: Math.round(fontMetrics.height * 1.4)
width: contentWidth * 1.2
background: Rectangle {
radius: parent.height / 2
- color: Nheko.colors.alternateBase
+ color: palette.alternateBase
}
}
diff --git a/resources/qml/delegates/Placeholder.qml b/resources/qml/delegates/Placeholder.qml
index 08008765..66e28c03 100644
--- a/resources/qml/delegates/Placeholder.qml
+++ b/resources/qml/delegates/Placeholder.qml
@@ -10,5 +10,5 @@ MatrixText {
text: qsTr("unimplemented event: ") + typeString
// width: parent.width
- color: Nheko.inactiveColors.text
+ color: palette.inactive.text
}
diff --git a/resources/qml/delegates/PlayableMediaMessage.qml b/resources/qml/delegates/PlayableMediaMessage.qml
index 741369d2..fb7bf0cc 100644
--- a/resources/qml/delegates/PlayableMediaMessage.qml
+++ b/resources/qml/delegates/PlayableMediaMessage.qml
@@ -4,11 +4,11 @@
import "../"
import "../ui/media"
-import QtMultimedia 5.15
-import QtQuick 2.15
-import QtQuick.Controls 2.15
-import QtQuick.Layouts 1.15
-import im.nheko 1.0
+import QtMultimedia
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import im.nheko
Item {
id: content
@@ -25,9 +25,9 @@ Item {
property double divisor: isReply ? 4 : 2
property int tempWidth: originalWidth < 1? 400: originalWidth
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
- implicitHeight: height
+ //implicitHeight: height
property int metadataWidth
property bool fitsMetadata: (parent.width - fileInfoLabel.width) > metadataWidth+4
@@ -36,18 +36,18 @@ Item {
id: mxcmedia
// TODO: Show error in overlay or so?
- onError: console.log(error)
roomm: room
- // desiredVolume is a float from 0.0 -> 1.0, MediaPlayer volume is an int from 0 to 100
- // this value automatically gets clamped for us between these two values.
- volume: mediaControls.desiredVolume * 100
- muted: mediaControls.muted
+ audioOutput: AudioOutput {
+ muted: mediaControls.muted
+ volume: mediaControls.desiredVolume
+ }
+ videoOutput: videoOutput
}
Rectangle {
id: videoContainer
- color: type == MtxEvent.VideoMessage ? Nheko.colors.window : "transparent"
+ color: type == MtxEvent.VideoMessage ? palette.window : "transparent"
width: parent.width
height: parent.height - fileInfoLabel.height
@@ -57,7 +57,7 @@ Item {
Image {
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
fillMode: Image.PreserveAspectFit
@@ -68,43 +68,40 @@ Item {
clip: true
anchors.fill: parent
fillMode: VideoOutput.PreserveAspectFit
- source: mxcmedia
- flushMode: VideoOutput.FirstFrame
orientation: mxcmedia.orientation
}
}
- }
+ MediaControls {
+ id: mediaControls
- MediaControls {
- id: mediaControls
-
- anchors.left: content.left
- anchors.right: content.right
- anchors.bottom: fileInfoLabel.top
- playingVideo: type == MtxEvent.VideoMessage
- positionValue: mxcmedia.position
- duration: mediaLoaded ? mxcmedia.duration : content.duration
- mediaLoaded: mxcmedia.loaded
- mediaState: mxcmedia.state
- onPositionChanged: mxcmedia.position = position
- onPlayPauseActivated: mxcmedia.state == MediaPlayer.PlayingState ? mxcmedia.pause() : mxcmedia.play()
- onLoadActivated: mxcmedia.eventId = eventId
+ anchors.left: videoContainer.left
+ anchors.right: videoContainer.right
+ anchors.bottom: videoContainer.bottom
+ playingVideo: type == MtxEvent.VideoMessage
+ positionValue: mxcmedia.position
+ duration: mediaLoaded ? mxcmedia.duration : content.duration
+ mediaLoaded: mxcmedia.loaded
+ mediaState: mxcmedia.playbackState
+ onPositionChanged: mxcmedia.position = position
+ onPlayPauseActivated: mxcmedia.playbackState == MediaPlayer.PlayingState ? mxcmedia.pause() : mxcmedia.play()
+ onLoadActivated: mxcmedia.eventId = eventId
+ }
}
// information about file name and file size
Label {
id: fileInfoLabel
- anchors.bottom: content.bottom
+ anchors.top: videoContainer.bottom
text: body + " [" + filesize + "]"
textFormat: Text.RichText
elide: Text.ElideRight
- color: Nheko.colors.text
+ color: palette.text
background: Rectangle {
- color: Nheko.colors.base
+ color: palette.base
}
}
diff --git a/resources/qml/delegates/Redacted.qml b/resources/qml/delegates/Redacted.qml
index 74d4e015..4a9700dc 100644
--- a/resources/qml/delegates/Redacted.qml
+++ b/resources/qml/delegates/Redacted.qml
@@ -13,7 +13,7 @@ Rectangle{
implicitWidth: redactedLayout.implicitWidth + 2 * Nheko.paddingMedium
width: Math.min(parent.width,implicitWidth+1)
radius: fontMetrics.lineSpacing / 2 + 2 * Nheko.paddingSmall
- color: Nheko.colors.alternateBase
+ color: palette.alternateBase
property int metadataWidth
property bool fitsMetadata: parent.width - redactedLayout.width > metadataWidth + 4
@@ -28,7 +28,7 @@ Rectangle{
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.preferredWidth: 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 {
id: redactedLabel
@@ -39,7 +39,7 @@ Rectangle{
property var redactedPair: room.formatRedactedEvent(eventId)
text: redactedPair["first"]
wrapMode: Label.WordWrap
- color: Nheko.colors.text
+ color: palette.text
ToolTip.text: redactedPair["second"]
ToolTip.visible: hh.hovered
diff --git a/resources/qml/delegates/Reply.qml b/resources/qml/delegates/Reply.qml
index c593a4f8..4d4983ac 100644
--- a/resources/qml/delegates/Reply.qml
+++ b/resources/qml/delegates/Reply.qml
@@ -43,7 +43,7 @@ AbstractButton {
implicitHeight: replyContainer.height
implicitWidth: visible? colorLine.width+Math.max(replyContainer.implicitWidth,userName_.fullTextWidth) : 0 // visible? seems to be causing issues
- CursorShape {
+ NhekoCursorShape {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
@@ -54,7 +54,7 @@ AbstractButton {
anchors.top: replyContainer.top
anchors.bottom: replyContainer.bottom
width: 4
- color: TimelineManager.userColor(userId, Nheko.colors.base)
+ color: TimelineManager.userColor(userId, palette.base)
}
onClicked: {
@@ -135,8 +135,8 @@ AbstractButton {
z: -1
anchors.fill: replyContainer
- property color userColor: TimelineManager.userColor(userId, Nheko.colors.base)
- property color bgColor: Nheko.colors.base
+ property color userColor: TimelineManager.userColor(userId, palette.base)
+ property color bgColor: palette.base
color: Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.1))
}
diff --git a/resources/qml/delegates/TextMessage.qml b/resources/qml/delegates/TextMessage.qml
index eb46a9ac..1eb5e2c0 100644
--- a/resources/qml/delegates/TextMessage.qml
+++ b/resources/qml/delegates/TextMessage.qml
@@ -14,20 +14,19 @@ MatrixText {
required property string formatted
property string copyText: selectedText ? getText(selectionStart, selectionEnd) : body
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
text: "
" + formatted.replace(//g, "").replace(/<\/del>/g, "").replace(//g, "").replace(/<\/strike>/g, "")
- width: parent.width
+ width: parent?.width ?? 0
height: !keepFullText ? Math.round(Math.min(timelineView.height / 8, implicitHeight)) : implicitHeight
clip: !keepFullText
selectByMouse: !Settings.mobileMode && !isReply
enabled: !Settings.mobileMode
font.pointSize: (Settings.enlargeEmojiOnlyMessages && isOnlyEmoji > 0 && isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
- CursorShape {
+ NhekoCursorShape {
enabled: isReply
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index d44fd9cf..afc6fd0a 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -15,8 +15,7 @@ ApplicationWindow {
onClosing: VerificationManager.removeVerificationFlow(flow)
title: stack.currentItem ? (stack.currentItem.title_ || "") : ""
modality: Qt.NonModal
- palette: Nheko.colors
- color: Nheko.colors.window
+ color: palette.window
//height: stack.currentItem.implicitHeight
minimumHeight: stack.currentItem.implicitHeight + 2 * Nheko.paddingLarge
height: stack.currentItem.implicitHeight + 2 * Nheko.paddingMedium
@@ -25,7 +24,7 @@ ApplicationWindow {
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
background: Rectangle {
- color: Nheko.colors.window
+ color: palette.window
}
diff --git a/resources/qml/device-verification/DigitVerification.qml b/resources/qml/device-verification/DigitVerification.qml
index 10ba4c55..33cc59f4 100644
--- a/resources/qml/device-verification/DigitVerification.qml
+++ b/resources/qml/device-verification/DigitVerification.qml
@@ -17,7 +17,7 @@ ColumnLayout {
Layout.fillWidth: true
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!")
- color: Nheko.colors.text
+ color: palette.text
verticalAlignment: Text.AlignVCenter
}
@@ -28,19 +28,19 @@ ColumnLayout {
Label {
font.pixelSize: Qt.application.font.pixelSize * 2
text: flow.sasList[0]
- color: Nheko.colors.text
+ color: palette.text
}
Label {
font.pixelSize: Qt.application.font.pixelSize * 2
text: flow.sasList[1]
- color: Nheko.colors.text
+ color: palette.text
}
Label {
font.pixelSize: Qt.application.font.pixelSize * 2
text: flow.sasList[2]
- color: Nheko.colors.text
+ color: palette.text
}
}
diff --git a/resources/qml/device-verification/EmojiVerification.qml b/resources/qml/device-verification/EmojiVerification.qml
index a6f6ff09..0ee279cd 100644
--- a/resources/qml/device-verification/EmojiVerification.qml
+++ b/resources/qml/device-verification/EmojiVerification.qml
@@ -17,7 +17,7 @@ ColumnLayout {
Layout.fillWidth: true
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!")
- color: Nheko.colors.text
+ color: palette.text
verticalAlignment: Text.AlignVCenter
}
@@ -373,13 +373,13 @@ ColumnLayout {
text: col.emoji.emoji
font.pixelSize: Qt.application.font.pixelSize * 2
font.family: Settings.emojiFont
- color: Nheko.colors.text
+ color: palette.text
}
Label {
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
text: col.emoji.description
- color: Nheko.colors.text
+ color: palette.text
}
}
@@ -396,7 +396,7 @@ ColumnLayout {
Layout.fillWidth: true
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.")
- color: Nheko.colors.text
+ color: palette.text
verticalAlignment: Text.AlignVCenter
}
diff --git a/resources/qml/device-verification/Failed.qml b/resources/qml/device-verification/Failed.qml
index fe514df0..5847894b 100644
--- a/resources/qml/device-verification/Failed.qml
+++ b/resources/qml/device-verification/Failed.qml
@@ -35,7 +35,7 @@ ColumnLayout {
return qsTr("Unknown verification error.");
}
}
- color: Nheko.colors.text
+ color: palette.text
verticalAlignment: Text.AlignVCenter
}
diff --git a/resources/qml/device-verification/NewVerificationRequest.qml b/resources/qml/device-verification/NewVerificationRequest.qml
index 84bee834..9a9ab703 100644
--- a/resources/qml/device-verification/NewVerificationRequest.qml
+++ b/resources/qml/device-verification/NewVerificationRequest.qml
@@ -36,7 +36,7 @@ ColumnLayout {
return qsTr("Your device (%1) has requested to be verified.").arg(flow.deviceId);
}
}
- color: Nheko.colors.text
+ color: palette.text
verticalAlignment: Text.AlignVCenter
}
diff --git a/resources/qml/device-verification/Success.qml b/resources/qml/device-verification/Success.qml
index 4e7bd5d1..4b60a5a3 100644
--- a/resources/qml/device-verification/Success.qml
+++ b/resources/qml/device-verification/Success.qml
@@ -19,7 +19,7 @@ ColumnLayout {
Layout.fillWidth: true
wrapMode: Text.Wrap
text: qsTr("Verification successful! Both sides verified their devices!")
- color: Nheko.colors.text
+ color: palette.text
verticalAlignment: Text.AlignVCenter
}
diff --git a/resources/qml/device-verification/Waiting.qml b/resources/qml/device-verification/Waiting.qml
index a7e68ae9..5b45fc7c 100644
--- a/resources/qml/device-verification/Waiting.qml
+++ b/resources/qml/device-verification/Waiting.qml
@@ -30,14 +30,14 @@ ColumnLayout {
return "";
}
}
- color: Nheko.colors.text
+ color: palette.text
verticalAlignment: Text.AlignVCenter
}
Item { Layout.fillHeight: true; }
Spinner {
Layout.alignment: Qt.AlignHCenter
- foreground: Nheko.colors.mid
+ foreground: palette.mid
}
Item { Layout.fillHeight: true; }
diff --git a/resources/qml/dialogs/AliasEditor.qml b/resources/qml/dialogs/AliasEditor.qml
index 8a79f7d2..c49ad321 100644
--- a/resources/qml/dialogs/AliasEditor.qml
+++ b/resources/qml/dialogs/AliasEditor.qml
@@ -41,7 +41,7 @@ ApplicationWindow {
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
Layout.fillWidth: true
Layout.fillHeight: false
- color: Nheko.colors.text
+ color: palette.text
Layout.bottomMargin: Nheko.paddingMedium
}
@@ -53,10 +53,6 @@ ApplicationWindow {
clip: true
- ScrollHelper {
- flickable: parent
- anchors.fill: parent
- }
model: editingModel
spacing: 4
@@ -69,7 +65,7 @@ ApplicationWindow {
Text {
Layout.fillWidth: true
text: model.name
- color: model.isPublished ? Nheko.colors.text : Nheko.theme.error
+ color: model.isPublished ? palette.text : Nheko.theme.error
textFormat: Text.PlainText
}
@@ -78,8 +74,8 @@ ApplicationWindow {
Layout.margins: 2
image: ":/icons/icons/ui/star.svg"
hoverEnabled: true
- buttonTextColor: model.isCanonical ? Nheko.colors.highlight : Nheko.colors.text
- highlightColor: editingModel.canAdvertize ? Nheko.colors.highlight : buttonTextColor
+ buttonTextColor: model.isCanonical ? palette.highlight : palette.text
+ highlightColor: editingModel.canAdvertize ? palette.highlight : buttonTextColor
ToolTip.visible: hovered
ToolTip.text: model.isCanonical ? qsTr("Primary alias") : qsTr("Make primary alias")
@@ -92,8 +88,8 @@ ApplicationWindow {
Layout.margins: 2
image: ":/icons/icons/ui/building-shop.svg"
hoverEnabled: true
- buttonTextColor: model.isAdvertized ? Nheko.colors.highlight : Nheko.colors.text
- highlightColor: editingModel.canAdvertize ? Nheko.colors.highlight : buttonTextColor
+ buttonTextColor: model.isAdvertized ? palette.highlight : palette.text
+ highlightColor: editingModel.canAdvertize ? palette.highlight : buttonTextColor
ToolTip.visible: hovered
ToolTip.text: qsTr("Advertise as an alias in this room")
@@ -106,7 +102,7 @@ ApplicationWindow {
Layout.margins: 2
image: ":/icons/icons/ui/room-directory.svg"
hoverEnabled: true
- buttonTextColor: model.isPublished ? Nheko.colors.highlight : Nheko.colors.text
+ buttonTextColor: model.isPublished ? palette.highlight : palette.text
ToolTip.visible: hovered
ToolTip.text: qsTr("Publish in room directory")
@@ -139,7 +135,7 @@ ApplicationWindow {
Layout.fillWidth: true
selectByMouse: true
font.pixelSize: fontMetrics.font.pixelSize
- color: Nheko.colors.text
+ color: palette.text
placeholderText: qsTr("#new-alias:server.tld")
Component.onCompleted: forceActiveFocus()
diff --git a/resources/qml/dialogs/AllowedRoomsSettingsDialog.qml b/resources/qml/dialogs/AllowedRoomsSettingsDialog.qml
index d93f1f18..89ea5e04 100644
--- a/resources/qml/dialogs/AllowedRoomsSettingsDialog.qml
+++ b/resources/qml/dialogs/AllowedRoomsSettingsDialog.qml
@@ -20,8 +20,7 @@ ApplicationWindow {
minimumHeight: 450
width: 450
height: 680
- palette: Nheko.colors
- color: Nheko.colors.window
+ color: palette.window
modality: Qt.NonModal
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
title: qsTr("Allowed rooms settings")
@@ -42,7 +41,7 @@ ApplicationWindow {
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
Layout.fillWidth: true
Layout.fillHeight: false
- color: Nheko.colors.text
+ color: palette.text
Layout.bottomMargin: Nheko.paddingMedium
}
@@ -54,10 +53,6 @@ ApplicationWindow {
clip: true
- ScrollHelper {
- flickable: parent
- anchors.fill: parent
- }
model: roomSettings.allowedRoomsModel
spacing: 4
@@ -72,14 +67,14 @@ ApplicationWindow {
Text {
Layout.fillWidth: true
text: model.name
- color: Nheko.colors.text
+ color: palette.text
textFormat: Text.PlainText
}
Text {
Layout.fillWidth: true
text: model.isParent ? qsTr("Parent community") : qsTr("Other room")
- color: Nheko.colors.buttonText
+ color: palette.buttonText
textFormat: Text.PlainText
}
}
@@ -122,7 +117,7 @@ ApplicationWindow {
placeholderText: qsTr("Enter additional rooms not in the list yet...")
- color: Nheko.colors.text
+ color: palette.text
onTextEdited: {
roomCompleter.completer.searchString = text;
}
diff --git a/resources/qml/dialogs/ConfirmJoinRoomDialog.qml b/resources/qml/dialogs/ConfirmJoinRoomDialog.qml
index b37630c8..a3fb9831 100644
--- a/resources/qml/dialogs/ConfirmJoinRoomDialog.qml
+++ b/resources/qml/dialogs/ConfirmJoinRoomDialog.qml
@@ -19,8 +19,7 @@ ApplicationWindow {
title: summary.isSpace ? qsTr("Confirm community join") : qsTr("Confirm room join")
modality: Qt.WindowModal
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
- palette: Nheko.colors
- color: Nheko.colors.window
+ color: palette.window
width: 350
height: content.implicitHeight + Nheko.paddingLarge + footer.implicitHeight
@@ -48,7 +47,7 @@ ApplicationWindow {
Spinner {
Layout.alignment: Qt.AlignHCenter
visible: !summary.isLoaded
- foreground: Nheko.colors.mid
+ foreground: palette.mid
running: !summary.isLoaded
}
@@ -57,7 +56,7 @@ ApplicationWindow {
textFormat: TextEdit.RichText
text: summary.roomName
font.pixelSize: fontMetrics.font.pixelSize * 2
- color: Nheko.colors.text
+ color: palette.text
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
@@ -70,7 +69,7 @@ ApplicationWindow {
textFormat: TextEdit.RichText
text: summary.roomid
font.pixelSize: fontMetrics.font.pixelSize * 0.8
- color: Nheko.colors.text
+ color: palette.text
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
@@ -96,7 +95,7 @@ ApplicationWindow {
readOnly: true
textFormat: TextEdit.RichText
text: summary.roomTopic
- color: Nheko.colors.text
+ color: palette.text
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
@@ -109,7 +108,7 @@ ApplicationWindow {
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:")
- color: Nheko.colors.text
+ color: palette.text
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
diff --git a/resources/qml/dialogs/CreateDirect.qml b/resources/qml/dialogs/CreateDirect.qml
index 4ce568bb..75013970 100644
--- a/resources/qml/dialogs/CreateDirect.qml
+++ b/resources/qml/dialogs/CreateDirect.qml
@@ -55,14 +55,14 @@ ApplicationWindow {
Label {
Layout.fillWidth: true
text: profile? profile.displayName : ""
- color: TimelineManager.userColor(userID.text, Nheko.colors.window)
+ color: TimelineManager.userColor(userID.text, palette.window)
font.pointSize: fontMetrics.font.pointSize
}
Label {
Layout.fillWidth: true
text: userID.text
- color: Nheko.colors.buttonText
+ color: palette.buttonText
font.pointSize: fontMetrics.font.pointSize * 0.9
}
}
@@ -89,7 +89,7 @@ ApplicationWindow {
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft
text: qsTr("Encryption")
- color: Nheko.colors.text
+ color: palette.text
}
ToggleButton {
Layout.alignment: Qt.AlignRight
diff --git a/resources/qml/dialogs/CreateRoom.qml b/resources/qml/dialogs/CreateRoom.qml
index cb198bb8..2164ba50 100644
--- a/resources/qml/dialogs/CreateRoom.qml
+++ b/resources/qml/dialogs/CreateRoom.qml
@@ -64,7 +64,7 @@ ApplicationWindow {
Label {
Layout.preferredWidth: implicitWidth
text: "#"
- color: Nheko.colors.text
+ color: palette.text
}
MatrixTextField {
id: newRoomAlias
@@ -75,14 +75,14 @@ ApplicationWindow {
Layout.preferredWidth: implicitWidth
property string userName: userInfoGrid.profile.userid
text: userName.substring(userName.indexOf(":"))
- color: Nheko.colors.text
+ color: palette.text
}
}
Label {
Layout.preferredWidth: implicitWidth
Layout.alignment: Qt.AlignLeft
text: qsTr("Public")
- color: Nheko.colors.text
+ color: palette.text
HoverHandler {
id: privateHover
}
@@ -101,7 +101,7 @@ ApplicationWindow {
Layout.preferredWidth: implicitWidth
Layout.alignment: Qt.AlignLeft
text: qsTr("Trusted")
- color: Nheko.colors.text
+ color: palette.text
HoverHandler {
id: trustedHover
}
@@ -122,7 +122,7 @@ ApplicationWindow {
Layout.preferredWidth: implicitWidth
Layout.alignment: Qt.AlignLeft
text: qsTr("Encryption")
- color: Nheko.colors.text
+ color: palette.text
HoverHandler {
id: encryptionHover
}
diff --git a/resources/qml/dialogs/EventExpirationDialog.qml b/resources/qml/dialogs/EventExpirationDialog.qml
new file mode 100644
index 00000000..5d12bda8
--- /dev/null
+++ b/resources/qml/dialogs/EventExpirationDialog.qml
@@ -0,0 +1,167 @@
+// SPDX-FileCopyrightText: Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import ".."
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import im.nheko
+
+ApplicationWindow {
+ id: dialog
+
+ property string roomid: ""
+ property string roomName: ""
+ property var onAccepted: undefined
+
+ modality: Qt.NonModal
+ flags: Qt.Dialog | Qt.WindowTitleHint
+ width: 275
+ height: 330
+ minimumWidth: 250
+ minimumHeight: 220
+
+ EventExpiry {
+ id: eventExpiry
+
+ roomid: dialog.roomid
+ }
+
+ title: {
+ if (roomid) {
+ return qsTr("Event expiration for %1").arg(roomName);
+ }
+ else {
+ return qsTr("Event expiration");
+ }
+ }
+
+ Shortcut {
+ sequence: StandardKey.Cancel
+ onActivated: dbb.rejected()
+ }
+
+ ColumnLayout {
+ spacing: Nheko.paddingMedium
+ anchors.margins: Nheko.paddingMedium
+ anchors.fill: parent
+
+ MatrixText {
+ id: promptLabel
+ text: {
+ if (roomid) {
+ return qsTr("You can configure when your messages will be deleted in %1. This only happens when Nheko is open and has permissions to delete messages until Matrix servers support this feature natively. In general 0 means disable.").arg(roomName);
+ }
+ else {
+ return qsTr("You can configure when your messages will be deleted in all rooms unless configured otherwise. This only happens when Nheko is open and has permissions to delete messages until Matrix servers support this feature natively. In general 0 means disable.");
+ }
+ }
+ font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.2)
+ Layout.fillWidth: true
+ Layout.fillHeight: false
+ }
+
+ GridLayout {
+ columns: 2
+ rowSpacing: Nheko.paddingMedium
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ MatrixText {
+ text: qsTr("Expire events after X days")
+ ToolTip.text: qsTr("Automatically redacts messages after X days, unless otherwise protected. Set to 0 to disable.")
+ ToolTip.visible: hh1.hovered
+ Layout.fillWidth: true
+
+ HoverHandler {
+ id: hh1
+ }
+ }
+
+ SpinBox {
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+ from: 0
+ to: 1000
+ stepSize: 1
+ value: eventExpiry.expireEventsAfterDays
+ onValueChanged: eventExpiry.expireEventsAfterDays = value
+ editable: true
+ }
+
+ MatrixText {
+ text: qsTr("Only keep latest X events")
+ ToolTip.text: qsTr("Deletes your events in this room if there are more than X newer messages unless otherwise protected. Set to 0 to disable.")
+ ToolTip.visible: hh2.hovered
+ Layout.fillWidth: true
+
+ HoverHandler {
+ id: hh2
+ }
+ }
+
+
+ SpinBox {
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+ from: 0
+ to: 1000
+ stepSize: 1
+ value: eventExpiry.expireEventsAfterCount
+ onValueChanged: eventExpiry.expireEventsAfterCount = value
+ editable: true
+ }
+
+ MatrixText {
+ text: qsTr("Always keep latest X events")
+ ToolTip.text: qsTr("This prevents events to be deleted by the above 2 settings if they are the latest X messages from you in the room.")
+ ToolTip.visible: hh3.hovered
+ Layout.fillWidth: true
+
+ HoverHandler {
+ id: hh3
+ }
+ }
+
+
+ SpinBox {
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+ from: 0
+ to: 1000
+ stepSize: 1
+ value: eventExpiry.protectLatestEvents
+ onValueChanged: eventExpiry.protectLatestEvents = value
+ editable: true
+ }
+
+ MatrixText {
+ text: qsTr("Include state events")
+ ToolTip.text: qsTr("If this is turned on, old state events also get redacted. The latest state event of any type+key combination is excluded from redaction to not remove the room name and similar state by accident.")
+ ToolTip.visible: hh4.hovered
+ Layout.fillWidth: true
+
+ HoverHandler {
+ id: hh4
+ }
+ }
+
+ ToggleButton {
+ Layout.alignment: Qt.AlignRight
+ checked: eventExpiry.expireStateEvents
+ onToggled: eventExpiry.expireStateEvents = checked
+ }
+ }
+ }
+
+ footer: DialogButtonBox {
+ id: dbb
+
+ standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
+ onAccepted: {
+ eventExpiry.save();
+ dialog.close();
+ }
+ onRejected: dialog.close();
+ }
+
+}
+
diff --git a/resources/qml/dialogs/ImageOverlay.qml b/resources/qml/dialogs/ImageOverlay.qml
index fa874529..b914829e 100644
--- a/resources/qml/dialogs/ImageOverlay.qml
+++ b/resources/qml/dialogs/ImageOverlay.qml
@@ -25,12 +25,12 @@ Window {
Component.onCompleted: Nheko.setWindowRole(imageOverlay, "imageoverlay")
Shortcut {
- sequence: StandardKey.Cancel
+ sequences: [StandardKey.Cancel]
onActivated: imageOverlay.close()
}
Shortcut {
- sequence: StandardKey.Copy
+ sequences: [StandardKey.Copy]
onActivated: {
if (room) {
room.copyMedia(eventId);
@@ -98,6 +98,10 @@ Window {
WheelHandler {
property: "scale"
+ // workaround for QTBUG-87646 / QTBUG-112394 / QTBUG-112432:
+ // Magic Mouse pretends to be a trackpad but doesn't work with PinchHandler
+ // and we don't yet distinguish mice and trackpads on Wayland either
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
target: imgContainer
}
diff --git a/resources/qml/dialogs/ImagePackEditorDialog.qml b/resources/qml/dialogs/ImagePackEditorDialog.qml
index 4f30e78a..4cb2c1f6 100644
--- a/resources/qml/dialogs/ImagePackEditorDialog.qml
+++ b/resources/qml/dialogs/ImagePackEditorDialog.qml
@@ -22,8 +22,7 @@ ApplicationWindow {
title: qsTr("Editing image pack")
height: 600
width: 600
- palette: Nheko.colors
- color: Nheko.colors.base
+ color: palette.base
modality: Qt.WindowModal
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
@@ -50,11 +49,6 @@ ApplicationWindow {
model: imagePack
- ScrollHelper {
- flickable: parent
- anchors.fill: parent
- enabled: !Settings.mobileMode
- }
header: AvatarListTile {
title: imagePack.packname
@@ -73,13 +67,12 @@ ApplicationWindow {
anchors.verticalCenter: parent.verticalCenter
height: parent.height - Nheko.paddingSmall * 2
width: 3
- color: Nheko.colors.highlight
+ color: palette.highlight
}
}
footer: Button {
- palette: Nheko.colors
onClicked: addFilesDialog.open()
width: ListView.view.width
text: qsTr("Add images")
@@ -100,11 +93,11 @@ ApplicationWindow {
delegate: AvatarListTile {
id: packItem
- property color background: 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
+ property color background: palette.window
+ property color importantText: palette.text
+ property color unimportantText: palette.buttonText
+ property color bubbleBackground: palette.highlight
+ property color bubbleText: palette.highlightedText
required property string shortCode
required property string url
required property string body
@@ -129,7 +122,7 @@ ApplicationWindow {
id: packinfoC
Rectangle {
- color: Nheko.colors.window
+ color: palette.window
GridLayout {
anchors.fill: parent
diff --git a/resources/qml/dialogs/ImagePackSettingsDialog.qml b/resources/qml/dialogs/ImagePackSettingsDialog.qml
index 76d84a07..b7aab2a6 100644
--- a/resources/qml/dialogs/ImagePackSettingsDialog.qml
+++ b/resources/qml/dialogs/ImagePackSettingsDialog.qml
@@ -23,8 +23,7 @@ ApplicationWindow {
title: qsTr("Image pack settings")
height: 600
width: 800
- palette: Nheko.colors
- color: Nheko.colors.base
+ color: palette.base
modality: Qt.NonModal
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
@@ -56,15 +55,10 @@ ApplicationWindow {
model: packlist
clip: true
- ScrollHelper {
- flickable: parent
- anchors.fill: parent
- enabled: !Settings.mobileMode
- }
+
footer: ColumnLayout {
Button {
- palette: Nheko.colors
onClicked: {
var dialog = packEditor.createObject(timelineRoot, {
"imagePack": packlist.newPack(false)
@@ -78,7 +72,6 @@ ApplicationWindow {
}
Button {
- palette: Nheko.colors
onClicked: {
var dialog = packEditor.createObject(timelineRoot, {
"imagePack": packlist.newPack(true)
@@ -96,11 +89,11 @@ ApplicationWindow {
delegate: AvatarListTile {
id: packItem
- property color background: 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
+ property color background: palette.window
+ property color importantText: palette.text
+ property color unimportantText: palette.buttonText
+ property color bubbleBackground: palette.highlight
+ property color bubbleText: palette.highlightedText
required property string displayName
required property bool fromAccountData
required property bool fromCurrentRoom
@@ -135,7 +128,7 @@ ApplicationWindow {
id: packinfoC
Rectangle {
- color: Nheko.colors.window
+ color: palette.window
ColumnLayout {
id: packinfo
@@ -220,11 +213,6 @@ ApplicationWindow {
currentIndex: -1 // prevent sorting from stealing focus
cacheBuffer: 500
- ScrollHelper {
- flickable: parent
- anchors.fill: parent
- enabled: !Settings.mobileMode
- }
// Individual emoji
delegate: AbstractButton {
@@ -243,7 +231,7 @@ ApplicationWindow {
background: Rectangle {
anchors.fill: parent
- color: hovered ? Nheko.colors.highlight : 'transparent'
+ color: hovered ? palette.highlight : 'transparent'
radius: 5
}
diff --git a/resources/qml/dialogs/InputDialog.qml b/resources/qml/dialogs/InputDialog.qml
index a4ca1683..49becc67 100644
--- a/resources/qml/dialogs/InputDialog.qml
+++ b/resources/qml/dialogs/InputDialog.qml
@@ -37,7 +37,7 @@ ApplicationWindow {
Label {
id: promptLabel
- color: Nheko.colors.text
+ color: palette.text
}
MatrixTextField {
diff --git a/resources/qml/dialogs/InviteDialog.qml b/resources/qml/dialogs/InviteDialog.qml
index b142818d..58bae7fd 100644
--- a/resources/qml/dialogs/InviteDialog.qml
+++ b/resources/qml/dialogs/InviteDialog.qml
@@ -40,8 +40,7 @@ ApplicationWindow {
title: qsTr("Invite users to %1").arg(invitees.room.plainRoomName)
height: 380
width: 340
- palette: Nheko.colors
- color: Nheko.colors.window
+ color: palette.window
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
Shortcut {
@@ -74,12 +73,12 @@ ApplicationWindow {
anchors.centerIn: parent
id: inviteeUserid
text: model.displayName != "" ? model.displayName : model.userid
- color: inviteeButton.hovered ? Nheko.colors.highlightedText: Nheko.colors.text
+ color: inviteeButton.hovered ? palette.highlightedText: palette.text
maximumLineCount: 1
}
background: Rectangle {
- border.color: Nheko.colors.text
- color: inviteeButton.hovered ? Nheko.colors.highlight : Nheko.colors.window
+ border.color: palette.text
+ color: inviteeButton.hovered ? palette.highlight : palette.window
border.width: 1
radius: inviteeButton.height / 2
}
@@ -90,7 +89,7 @@ ApplicationWindow {
Label {
text: qsTr("Search user")
Layout.fillWidth: true
- color: Nheko.colors.text
+ color: palette.text
}
RowLayout {
spacing: Nheko.paddingMedium
@@ -100,7 +99,7 @@ ApplicationWindow {
property bool isValidMxid: text.match("@.+?:.{3,}")
- backgroundColor: Nheko.colors.window
+ backgroundColor: palette.window
placeholderText: qsTr("@joe:matrix.org", "Example user id. The name 'joe' can be localized however you want.")
Layout.fillWidth: true
onAccepted: {
@@ -158,7 +157,7 @@ ApplicationWindow {
avatarUrl: profile? profile.avatarUrl : ""
userid: inviteeEntry.text
onClicked: addInvite(inviteeEntry.text, displayName, avatarUrl)
- bgColor: del3.hovered ? Nheko.colors.dark : inviteDialogRoot.color
+ bgColor: del3.hovered ? palette.dark : inviteDialogRoot.color
}
ListView {
visible: !inviteeEntry.isValidMxid
@@ -175,7 +174,7 @@ ApplicationWindow {
userid: model.userid
avatarUrl: model.avatarUrl
onClicked: addInvite(userid, displayName, avatarUrl)
- bgColor: del2.hovered ? Nheko.colors.dark : inviteDialogRoot.color
+ bgColor: del2.hovered ? palette.dark : inviteDialogRoot.color
}
}
Rectangle {
@@ -202,7 +201,7 @@ ApplicationWindow {
userid: model.mxid
avatarUrl: model.avatarUrl
displayName: model.displayName
- bgColor: del.hovered ? Nheko.colors.dark : inviteDialogRoot.color
+ bgColor: del.hovered ? palette.dark : inviteDialogRoot.color
ImageButton {
anchors.right: parent.right
anchors.rightMargin: Nheko.paddingSmall
@@ -213,7 +212,7 @@ ApplicationWindow {
onClicked: invitees.removeUser(model.mxid)
}
- CursorShape {
+ NhekoCursorShape {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
diff --git a/resources/qml/dialogs/JoinRoomDialog.qml b/resources/qml/dialogs/JoinRoomDialog.qml
index 57c7bad8..0974325a 100644
--- a/resources/qml/dialogs/JoinRoomDialog.qml
+++ b/resources/qml/dialogs/JoinRoomDialog.qml
@@ -14,8 +14,7 @@ ApplicationWindow {
title: qsTr("Join room")
modality: Qt.WindowModal
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
- palette: Nheko.colors
- color: Nheko.colors.window
+ color: palette.window
width: 350
height: fontMetrics.lineSpacing * 7
@@ -33,7 +32,7 @@ ApplicationWindow {
id: promptLabel
text: qsTr("Room ID or alias")
- color: Nheko.colors.text
+ color: palette.text
}
MatrixTextField {
diff --git a/resources/qml/dialogs/PhoneNumberInputDialog.qml b/resources/qml/dialogs/PhoneNumberInputDialog.qml
index f7719800..f1b8eef8 100644
--- a/resources/qml/dialogs/PhoneNumberInputDialog.qml
+++ b/resources/qml/dialogs/PhoneNumberInputDialog.qml
@@ -31,7 +31,7 @@ ApplicationWindow {
id: promptLabel
Layout.columnSpan: 2
- color: Nheko.colors.text
+ color: palette.text
}
ComboBox {
diff --git a/resources/qml/dialogs/PowerLevelEditor.qml b/resources/qml/dialogs/PowerLevelEditor.qml
index 7125c712..9fc9ee15 100644
--- a/resources/qml/dialogs/PowerLevelEditor.qml
+++ b/resources/qml/dialogs/PowerLevelEditor.qml
@@ -41,14 +41,13 @@ ApplicationWindow {
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
Layout.fillWidth: true
Layout.fillHeight: false
- color: Nheko.colors.text
+ color: palette.text
Layout.bottomMargin: Nheko.paddingMedium
}
TabBar {
id: bar
width: parent.width
- palette: Nheko.colors
NhekoTabButton {
text: qsTr("Roles")
@@ -60,7 +59,7 @@ ApplicationWindow {
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
- color: Nheko.colors.alternateBase
+ color: palette.alternateBase
border.width: 1
border.color: Nheko.theme.separator
@@ -78,7 +77,7 @@ ApplicationWindow {
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
Layout.fillWidth: true
Layout.fillHeight: false
- color: Nheko.colors.text
+ color: palette.text
}
ReorderableListview {
@@ -91,7 +90,7 @@ ApplicationWindow {
Column {
Layout.fillWidth: true
- Text { visible: model.isType; text: model.displayName; color: Nheko.colors.text}
+ Text { visible: model.isType; text: model.displayName; color: palette.text}
Text {
visible: !model.isType;
text: {
@@ -104,7 +103,7 @@ ApplicationWindow {
else
return qsTr("Custom (%1)").arg(model.powerlevel)
}
- color: Nheko.colors.text
+ color: palette.text
}
}
@@ -137,7 +136,7 @@ ApplicationWindow {
z: 5
visible: false
- color: Nheko.colors.text
+ color: palette.text
Keys.onPressed: {
if (typeEntry.text.includes('.') && event.matches(StandardKey.InsertParagraphSeparator)) {
@@ -166,7 +165,7 @@ ApplicationWindow {
anchors.fill: parent
visible: false
- color: Nheko.colors.alternateBase
+ color: palette.alternateBase
RowLayout {
spacing: Nheko.paddingMedium
@@ -238,7 +237,7 @@ ApplicationWindow {
width: parent.width
//font.pixelSize: Math.ceil(quickSwitcher.textHeight * 0.6)
- color: Nheko.colors.text
+ color: palette.text
onTextEdited: {
userCompleter.completer.searchString = text;
}
@@ -318,11 +317,11 @@ ApplicationWindow {
if (model.isUser)
return model.avatarUrl.replace("mxc://", "image://MxcImage/")
else if (editingModel.adminLevel >= model.powerlevel)
- return "image://colorimage/:/icons/icons/ui/ribbon_star.svg?" + Nheko.colors.buttonText;
+ return "image://colorimage/:/icons/icons/ui/ribbon_star.svg?" + palette.buttonText;
else if (editingModel.moderatorLevel >= model.powerlevel)
- return "image://colorimage/:/icons/icons/ui/ribbon.svg?" + Nheko.colors.buttonText;
+ return "image://colorimage/:/icons/icons/ui/ribbon.svg?" + palette.buttonText;
else
- return "image://colorimage/:/icons/icons/ui/person.svg?" + Nheko.colors.buttonText;
+ return "image://colorimage/:/icons/icons/ui/person.svg?" + palette.buttonText;
}
displayName: model.displayName
enabled: false
@@ -330,8 +329,8 @@ ApplicationWindow {
Column {
Layout.fillWidth: true
- Text { visible: model.isUser; text: model.displayName; color: Nheko.colors.text}
- Text { visible: model.isUser; text: model.mxid; color: Nheko.colors.text}
+ Text { visible: model.isUser; text: model.displayName; color: palette.text}
+ Text { visible: model.isUser; text: model.mxid; color: palette.text}
Text {
visible: !model.isUser;
text: {
@@ -342,7 +341,7 @@ ApplicationWindow {
else
return qsTr("Custom (%1)").arg(model.powerlevel)
}
- color: Nheko.colors.text
+ color: palette.text
}
}
diff --git a/resources/qml/dialogs/PowerLevelSpacesApplyDialog.qml b/resources/qml/dialogs/PowerLevelSpacesApplyDialog.qml
index e66f92a2..01ec8b61 100644
--- a/resources/qml/dialogs/PowerLevelSpacesApplyDialog.qml
+++ b/resources/qml/dialogs/PowerLevelSpacesApplyDialog.qml
@@ -21,8 +21,7 @@ ApplicationWindow {
minimumHeight: 450
width: 450
height: 680
- palette: Nheko.colors
- color: Nheko.colors.window
+ color: palette.window
modality: Qt.NonModal
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
title: qsTr("Apply permission changes")
@@ -43,7 +42,7 @@ ApplicationWindow {
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 1.1)
Layout.fillWidth: true
Layout.fillHeight: false
- color: Nheko.colors.text
+ color: palette.text
Layout.bottomMargin: Nheko.paddingMedium
}
@@ -55,7 +54,7 @@ ApplicationWindow {
Label {
text: qsTr("Apply permissions recursively")
Layout.fillWidth: true
- color: Nheko.colors.text
+ color: palette.text
}
ToggleButton {
@@ -67,7 +66,7 @@ ApplicationWindow {
Label {
text: qsTr("Overwrite exisiting modifications in rooms")
Layout.fillWidth: true
- color: Nheko.colors.text
+ color: palette.text
}
ToggleButton {
@@ -85,11 +84,6 @@ ApplicationWindow {
clip: true
- ScrollHelper {
- flickable: parent
- anchors.fill: parent
- }
-
model: editingModel.spaces
spacing: 4
cacheBuffer: 50
@@ -103,7 +97,7 @@ ApplicationWindow {
Text {
Layout.fillWidth: true
text: model.displayName
- color: Nheko.colors.text
+ color: palette.text
textFormat: Text.PlainText
elide: Text.ElideRight
}
@@ -117,7 +111,7 @@ ApplicationWindow {
return qsTr("Permissions synchronized with community")
}
elide: Text.ElideRight
- color: Nheko.colors.buttonText
+ color: palette.buttonText
textFormat: Text.PlainText
}
}
diff --git a/resources/qml/dialogs/RawMessageDialog.qml b/resources/qml/dialogs/RawMessageDialog.qml
index ef7159cb..a27d988e 100644
--- a/resources/qml/dialogs/RawMessageDialog.qml
+++ b/resources/qml/dialogs/RawMessageDialog.qml
@@ -13,8 +13,7 @@ ApplicationWindow {
height: 420
width: 420
- palette: Nheko.colors
- color: Nheko.colors.window
+ color: palette.window
flags: Qt.Tool | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
Shortcut {
@@ -25,14 +24,13 @@ ApplicationWindow {
ScrollView {
anchors.margins: Nheko.paddingMedium
anchors.fill: parent
- palette: Nheko.colors
padding: Nheko.paddingMedium
TextArea {
id: rawMessageView
font: Nheko.monospaceFont()
- color: Nheko.colors.text
+ color: palette.text
readOnly: true
selectByMouse: !Settings.mobileMode
textFormat: Text.PlainText
@@ -40,7 +38,7 @@ ApplicationWindow {
anchors.fill: parent
background: Rectangle {
- color: Nheko.colors.base
+ color: palette.base
}
}
diff --git a/resources/qml/dialogs/ReadReceipts.qml b/resources/qml/dialogs/ReadReceipts.qml
index 3d23a5fc..d65de73c 100644
--- a/resources/qml/dialogs/ReadReceipts.qml
+++ b/resources/qml/dialogs/ReadReceipts.qml
@@ -18,8 +18,7 @@ ApplicationWindow {
width: 340
minimumHeight: 380
minimumWidth: headerTitle.width + 2 * Nheko.paddingMedium
- palette: Nheko.colors
- color: Nheko.colors.window
+ color: palette.window
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
Shortcut {
@@ -35,14 +34,13 @@ ApplicationWindow {
Label {
id: headerTitle
- color: Nheko.colors.text
+ color: palette.text
Layout.alignment: Qt.AlignCenter
text: qsTr("Read receipts")
font.pointSize: fontMetrics.font.pointSize * 1.5
}
ScrollView {
- palette: Nheko.colors
padding: Nheko.paddingMedium
ScrollBar.horizontal.visible: false
Layout.fillHeight: true
@@ -67,7 +65,7 @@ ApplicationWindow {
ToolTip.visible: hovered
ToolTip.text: model.mxid
background: Rectangle {
- color: del.hovered ? Nheko.colors.dark : readReceiptsRoot.color
+ color: del.hovered ? palette.dark : readReceiptsRoot.color
}
RowLayout {
@@ -93,16 +91,16 @@ ApplicationWindow {
Layout.fillWidth: true
ElidedLabel {
- text: model.displayName
- color: TimelineManager.userColor(model ? model.mxid : "", Nheko.colors.window)
+ fullText: model.displayName
+ color: TimelineManager.userColor(model ? model.mxid : "", palette.window)
font.pointSize: fontMetrics.font.pointSize
elideWidth: del.width - Nheko.paddingMedium - avatar.width
Layout.fillWidth: true
}
ElidedLabel {
- text: model.timestamp
- color: Nheko.colors.buttonText
+ fullText: model.timestamp
+ color: palette.buttonText
font.pointSize: fontMetrics.font.pointSize * 0.9
elideWidth: del.width - Nheko.paddingMedium - avatar.width
Layout.fillWidth: true
@@ -112,7 +110,7 @@ ApplicationWindow {
}
- CursorShape {
+ NhekoCursorShape {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
diff --git a/resources/qml/dialogs/RoomDirectory.qml b/resources/qml/dialogs/RoomDirectory.qml
index 85de9b45..a6f53d2e 100644
--- a/resources/qml/dialogs/RoomDirectory.qml
+++ b/resources/qml/dialogs/RoomDirectory.qml
@@ -18,8 +18,7 @@ ApplicationWindow {
minimumHeight: 340
height: 420
width: 650
- palette: Nheko.colors
- color: Nheko.colors.window
+ color: palette.window
modality: Qt.NonModal
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
title: qsTr("Explore Public Rooms")
@@ -35,17 +34,12 @@ ApplicationWindow {
anchors.fill: parent
model: publicRooms
- ScrollHelper {
- flickable: parent
- anchors.fill: parent
- }
-
delegate: Rectangle {
id: roomDirDelegate
- property color background: Nheko.colors.window
- property color importantText: Nheko.colors.text
- property color unimportantText: Nheko.colors.buttonText
+ property color background: palette.window
+ property color importantText: palette.text
+ property color unimportantText: palette.buttonText
property int avatarSize: fontMetrics.height * 3.2
color: background
@@ -143,7 +137,7 @@ ApplicationWindow {
anchors.centerIn: parent
anchors.margins: Nheko.paddingLarge
running: visible
- foreground: Nheko.colors.mid
+ foreground: palette.mid
}
}
@@ -164,7 +158,7 @@ ApplicationWindow {
Layout.fillWidth: true
selectByMouse: true
font.pixelSize: fontMetrics.font.pixelSize
- color: Nheko.colors.text
+ color: palette.text
placeholderText: qsTr("Search for public rooms")
onTextChanged: searchTimer.restart()
@@ -176,7 +170,7 @@ ApplicationWindow {
Layout.minimumWidth: 0.3 * header.width
Layout.maximumWidth: 0.3 * header.width
- color: Nheko.colors.text
+ color: palette.text
placeholderText: qsTr("Choose custom homeserver")
onTextChanged: publicRooms.setMatrixServer(text)
}
diff --git a/resources/qml/dialogs/RoomMembers.qml b/resources/qml/dialogs/RoomMembers.qml
index 1cfbe077..bbf1605d 100644
--- a/resources/qml/dialogs/RoomMembers.qml
+++ b/resources/qml/dialogs/RoomMembers.qml
@@ -20,8 +20,7 @@ ApplicationWindow {
height: 650
width: 420
minimumHeight: 420
- palette: Nheko.colors
- color: Nheko.colors.window
+ color: palette.window
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
Shortcut {
@@ -77,7 +76,7 @@ ApplicationWindow {
Label {
text: qsTr("Sort by: ")
- color: Nheko.colors.text
+ color: palette.text
}
ComboBox {
@@ -94,7 +93,6 @@ ApplicationWindow {
}
ScrollView {
- palette: Nheko.colors
padding: Nheko.paddingMedium
ScrollBar.horizontal.visible: false
Layout.fillHeight: true
@@ -108,11 +106,6 @@ ApplicationWindow {
boundsBehavior: Flickable.StopAtBounds
model: members
- ScrollHelper {
- flickable: parent
- anchors.fill: parent
- enabled: !Settings.mobileMode
- }
delegate: ItemDelegate {
id: del
@@ -123,7 +116,7 @@ ApplicationWindow {
height: memberLayout.implicitHeight + Nheko.paddingSmall * 2
hoverEnabled: true
background: Rectangle {
- color: del.hovered ? Nheko.colors.dark : roomMembersRoot.color
+ color: del.hovered ? palette.dark : roomMembersRoot.color
}
RowLayout {
@@ -158,7 +151,7 @@ ApplicationWindow {
ElidedLabel {
fullText: model.mxid
- color: del.hovered ? Nheko.colors.brightText : Nheko.colors.buttonText
+ color: del.hovered ? palette.brightText : palette.buttonText
font.pixelSize: Math.ceil(fontMetrics.font.pixelSize * 0.9)
elideWidth: del.width - Nheko.paddingMedium * 2 - avatar.width - encryptInd.width
Layout.fillWidth: true
@@ -184,7 +177,7 @@ ApplicationWindow {
Layout.preferredHeight: 16
sourceSize.width: width
sourceSize.height: height
- source: sourceUrl + (ma.hovered ? Nheko.colors.highlight : Nheko.colors.buttonText)
+ source: sourceUrl + (ma.hovered ? palette.highlight : palette.buttonText)
ToolTip.visible: ma.hovered
ToolTip.text: {
if (isAdmin)
@@ -227,7 +220,7 @@ ApplicationWindow {
}
- CursorShape {
+ NhekoCursorShape {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
diff --git a/resources/qml/dialogs/RoomSettings.qml b/resources/qml/dialogs/RoomSettings.qml
index 845f4d7a..3b8e1903 100644
--- a/resources/qml/dialogs/RoomSettings.qml
+++ b/resources/qml/dialogs/RoomSettings.qml
@@ -20,8 +20,7 @@ ApplicationWindow {
minimumHeight: 450
width: 450
height: 680
- palette: Nheko.colors
- color: Nheko.colors.window
+ color: palette.window
modality: Qt.NonModal
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
title: qsTr("Room Settings")
@@ -30,10 +29,7 @@ ApplicationWindow {
sequence: StandardKey.Cancel
onActivated: roomSettingsDialog.close()
}
- ScrollHelper {
- flickable: flickable
- anchors.fill: flickable
- }
+
Flickable {
id: flickable
boundsBehavior: Flickable.StopAtBounds
@@ -79,7 +75,7 @@ ApplicationWindow {
Spinner {
Layout.alignment: Qt.AlignHCenter
visible: roomSettings.isLoading
- foreground: Nheko.colors.mid
+ foreground: palette.mid
running: roomSettings.isLoading
}
@@ -130,7 +126,7 @@ ApplicationWindow {
textFormat: isNameEditingAllowed ? TextEdit.PlainText : TextEdit.RichText
text: isNameEditingAllowed ? roomSettings.plainRoomName : roomSettings.roomName
font.pixelSize: fontMetrics.font.pixelSize * 2
- color: Nheko.colors.text
+ color: palette.text
Layout.alignment: Qt.AlignHCenter
Layout.maximumWidth: parent.width - (Nheko.paddingSmall * 2) - nameChangeButton.anchors.leftMargin - (nameChangeButton.width * 2)
@@ -178,7 +174,7 @@ ApplicationWindow {
Label {
text: qsTr("%n member(s)", "", roomSettings.memberCount)
- color: Nheko.colors.text
+ color: palette.text
}
ImageButton {
@@ -213,11 +209,11 @@ ApplicationWindow {
wrapMode: TextEdit.WordWrap
background: null
selectByMouse: !Settings.mobileMode
- color: Nheko.colors.text
+ color: palette.text
horizontalAlignment: TextEdit.AlignHCenter
onLinkActivated: Nheko.openLink(link)
- CursorShape {
+ NhekoCursorShape {
anchors.fill: parent
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
@@ -261,19 +257,18 @@ ApplicationWindow {
Layout.fillWidth: true
Label {
- text: qsTr("SETTINGS")
+ text: qsTr("NOTIFICATIONS")
font.bold: true
- color: Nheko.colors.text
- }
-
- Item {
+ color: palette.text
+ Layout.columnSpan: 2
Layout.fillWidth: true
+ Layout.topMargin: Nheko.paddingLarge
}
Label {
text: qsTr("Notifications")
Layout.fillWidth: true
- color: Nheko.colors.text
+ color: palette.text
}
ComboBox {
@@ -286,10 +281,19 @@ ApplicationWindow {
WheelHandler{} // suppress scrolling changing values
}
+ Label {
+ text: qsTr("ENTRY PERMISSIONS")
+ font.bold: true
+ color: palette.text
+ Layout.columnSpan: 2
+ Layout.fillWidth: true
+ Layout.topMargin: Nheko.paddingLarge
+ }
+
Label {
text: qsTr("Anyone can join")
Layout.fillWidth: true
- color: Nheko.colors.text
+ color: palette.text
}
ToggleButton {
@@ -303,7 +307,7 @@ ApplicationWindow {
Label {
text: qsTr("Allow knocking")
Layout.fillWidth: true
- color: Nheko.colors.text
+ color: palette.text
visible: knockingButton.visible
}
@@ -322,7 +326,7 @@ ApplicationWindow {
Label {
text: qsTr("Allow joining via other rooms")
Layout.fillWidth: true
- color: Nheko.colors.text
+ color: palette.text
visible: restrictedButton.visible
}
@@ -341,7 +345,7 @@ ApplicationWindow {
Label {
text: qsTr("Rooms to join via")
Layout.fillWidth: true
- color: Nheko.colors.text
+ color: palette.text
visible: allowedRoomsButton.visible
}
@@ -360,7 +364,7 @@ ApplicationWindow {
Label {
text: qsTr("Allow guests to join")
Layout.fillWidth: true
- color: Nheko.colors.text
+ color: palette.text
}
ToggleButton {
@@ -381,9 +385,152 @@ ApplicationWindow {
Layout.fillWidth: true
}
+ Label {
+ text: qsTr("MESSAGE VISIBILITY")
+ font.bold: true
+ color: palette.text
+ Layout.columnSpan: 2
+ Layout.fillWidth: true
+ Layout.topMargin: Nheko.paddingLarge
+ }
+
+ Label {
+ text: qsTr("Allow viewing history without joining")
+ Layout.fillWidth: true
+ color: palette.text
+ ToolTip.text: qsTr("This is useful to see previews of the room or view it on public websites.")
+ ToolTip.visible: publicHistoryHover.hovered
+ ToolTip.delay: Nheko.tooltipDelay
+
+ HoverHandler {
+ id: publicHistoryHover
+
+ }
+ }
+
+ ToggleButton {
+ id: publicHistoryButton
+
+ enabled: roomSettings.canChangeHistoryVisibility
+ checked: roomSettings.historyVisibility == RoomSettings.WorldReadable
+ Layout.alignment: Qt.AlignRight
+ }
+
+ Label {
+ visible: !publicHistoryButton.checked
+ text: qsTr("Members can see messages since")
+ Layout.fillWidth: true
+ color: palette.text
+ Layout.alignment: Qt.AlignTop | Qt.AlignLeft
+ ToolTip.text: qsTr("How much of the history is visible to joined members. Changing this won't affect the visibility of already sent messages. It only applies to new messages.")
+ ToolTip.visible: privateHistoryHover.hovered
+ ToolTip.delay: Nheko.tooltipDelay
+
+ HoverHandler {
+ id: privateHistoryHover
+
+ }
+ }
+
+ ColumnLayout {
+ Layout.fillWidth: true
+ visible: !publicHistoryButton.checked
+ enabled: roomSettings.canChangeHistoryVisibility
+ Layout.alignment: Qt.AlignTop | Qt.AlignRight
+
+ RadioButton {
+ id: sharedHistory
+ checked: roomSettings.historyVisibility == RoomSettings.Shared
+ text: qsTr("Everything")
+ ToolTip.text: qsTr("As long as the user joined, they can see all previous messages.")
+ ToolTip.visible: hovered
+ ToolTip.delay: Nheko.tooltipDelay
+ }
+ RadioButton {
+ id: invitedHistory
+ checked: roomSettings.historyVisibility == RoomSettings.Invited
+ text: qsTr("They got invited")
+ ToolTip.text: qsTr("Members can only see messages from when they got invited going forward.")
+ ToolTip.visible: hovered
+ ToolTip.delay: Nheko.tooltipDelay
+ }
+ RadioButton {
+ id: joinedHistory
+ checked: roomSettings.historyVisibility == RoomSettings.Joined || roomSettings.historyVisibility == RoomSettings.WorldReadable
+ text: qsTr("They joined")
+ ToolTip.text: qsTr("Members can only see messages since after they joined.")
+ ToolTip.visible: hovered
+ ToolTip.delay: Nheko.tooltipDelay
+ }
+ }
+
+ Button {
+ visible: roomSettings.historyVisibility != selectedVisibility
+ enabled: roomSettings.canChangeHistoryVisibility
+
+ text: qsTr("Apply visibility changes")
+ property int selectedVisibility: {
+ if (publicHistoryButton.checked)
+ return RoomSettings.WorldReadable;
+ else if (sharedHistory.checked)
+ return RoomSettings.Shared;
+ else if (invitedHistory.checked)
+ return RoomSettings.Invited;
+ return RoomSettings.Joined;
+ }
+ onClicked: roomSettings.changeHistoryVisibility(selectedVisibility)
+ Layout.columnSpan: 2
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: qsTr("Locally hidden events")
+ color: palette.text
+ }
+
+ HiddenEventsDialog {
+ id: hiddenEventsDialog
+ roomid: roomSettings.roomId
+ roomName: roomSettings.roomName
+ }
+
+ Button {
+ text: qsTr("Configure")
+ ToolTip.text: qsTr("Select events to hide in this room")
+ onClicked: hiddenEventsDialog.show()
+ Layout.alignment: Qt.AlignRight
+ }
+
+ Label {
+ text: qsTr("Automatic event deletion")
+ color: palette.text
+ }
+
+ EventExpirationDialog {
+ id: eventExpirationDialog
+ roomid: roomSettings.roomId
+ roomName: roomSettings.roomName
+ }
+
+ Button {
+ text: qsTr("Configure")
+ ToolTip.text: qsTr("Select if your events get automatically deleted in this room.")
+ onClicked: eventExpirationDialog.show()
+ Layout.alignment: Qt.AlignRight
+ }
+
+ Label {
+ text: qsTr("GENERAL SETTINGS")
+ font.bold: true
+ color: palette.text
+ Layout.columnSpan: 2
+ Layout.fillWidth: true
+ Layout.topMargin: Nheko.paddingLarge
+ }
+
Label {
text: qsTr("Encryption")
- color: Nheko.colors.text
+ color: palette.text
}
ToggleButton {
@@ -422,7 +569,7 @@ ApplicationWindow {
Label {
text: qsTr("Permission")
- color: Nheko.colors.text
+ color: palette.text
}
Button {
@@ -434,7 +581,7 @@ ApplicationWindow {
Label {
text: qsTr("Aliases")
- color: Nheko.colors.text
+ color: palette.text
}
Button {
@@ -446,7 +593,7 @@ ApplicationWindow {
Label {
text: qsTr("Sticker & Emote Settings")
- color: Nheko.colors.text
+ color: palette.text
}
Button {
@@ -456,47 +603,18 @@ ApplicationWindow {
Layout.alignment: Qt.AlignRight
}
- Label {
- text: qsTr("Hidden events")
- color: Nheko.colors.text
- }
-
- HiddenEventsDialog {
- id: hiddenEventsDialog
- roomid: roomSettings.roomId
- roomName: roomSettings.roomName
- }
-
- Button {
- text: qsTr("Configure")
- ToolTip.text: qsTr("Select events to hide in this room")
- onClicked: hiddenEventsDialog.show()
- Layout.alignment: Qt.AlignRight
- }
-
- Item {
- // for adding extra space between sections
- Layout.fillWidth: true
- }
-
- Item {
- // for adding extra space between sections
- Layout.fillWidth: true
- }
-
Label {
text: qsTr("INFO")
font.bold: true
- color: Nheko.colors.text
- }
-
- Item {
+ color: palette.text
+ Layout.columnSpan: 2
+ Layout.topMargin: Nheko.paddingLarge
Layout.fillWidth: true
}
Label {
text: qsTr("Internal ID")
- color: Nheko.colors.text
+ color: palette.text
}
AbstractButton { // AbstractButton does not allow setting text color
@@ -507,7 +625,7 @@ ApplicationWindow {
id: idLabel
text: roomSettings.roomId
font.pixelSize: Math.floor(fontMetrics.font.pixelSize * 0.8)
- color: Nheko.colors.text
+ color: palette.text
width: parent.width
horizontalAlignment: Text.AlignRight
wrapMode: Text.WrapAnywhere
@@ -531,14 +649,14 @@ ApplicationWindow {
Label {
text: qsTr("Room Version")
- color: Nheko.colors.text
+ color: palette.text
}
Label {
text: roomSettings.roomVersion
font.pixelSize: fontMetrics.font.pixelSize
Layout.alignment: Qt.AlignRight
- color: Nheko.colors.text
+ color: palette.text
}
}
diff --git a/resources/qml/dialogs/UserProfile.qml b/resources/qml/dialogs/UserProfile.qml
index 95800746..b54b52a4 100644
--- a/resources/qml/dialogs/UserProfile.qml
+++ b/resources/qml/dialogs/UserProfile.qml
@@ -22,8 +22,7 @@ ApplicationWindow {
width: 420
minimumWidth: 150
minimumHeight: 150
- palette: Nheko.colors
- color: Nheko.colors.window
+ color: palette.window
title: profile.isGlobalUserProfile ? qsTr("Global User Profile") : qsTr("Room User Profile")
modality: Qt.NonModal
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
@@ -47,12 +46,6 @@ ApplicationWindow {
anchors.margins: 10
footerPositioning: ListView.OverlayFooter
- ScrollHelper {
- flickable: parent
- anchors.fill: parent
- enabled: !Settings.mobileMode
- }
-
header: ColumnLayout {
id: contentL
@@ -89,7 +82,7 @@ ApplicationWindow {
Layout.alignment: Qt.AlignHCenter
running: profile.isLoading
visible: profile.isLoading
- foreground: Nheko.colors.mid
+ foreground: palette.mid
}
Text {
@@ -137,7 +130,7 @@ ApplicationWindow {
readOnly: !isUsernameEditingAllowed
text: profile.displayName
font.pixelSize: 20
- color: TimelineManager.userColor(profile.userid, Nheko.colors.window)
+ color: TimelineManager.userColor(profile.userid, palette.window)
font.bold: true
Layout.alignment: Qt.AlignHCenter
Layout.maximumWidth: parent.width - (Nheko.paddingSmall * 2) - usernameChangeButton.anchors.leftMargin - (usernameChangeButton.width * 2)
@@ -315,7 +308,6 @@ ApplicationWindow {
onCurrentIndexChanged: devicelist.selectedTab = currentIndex
- palette: Nheko.colors
NhekoTabButton {
text: qsTr("Devices")
@@ -354,7 +346,7 @@ ApplicationWindow {
Layout.alignment: Qt.AlignLeft
elide: Text.ElideRight
font.bold: true
- color: Nheko.colors.text
+ color: palette.text
text: deviceId
}
@@ -400,7 +392,7 @@ ApplicationWindow {
readOnly: !deviceNameRow.isEditingAllowed
text: deviceName
- color: Nheko.colors.text
+ color: palette.text
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
selectByMouse: true
@@ -435,7 +427,7 @@ ApplicationWindow {
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft
elide: Text.ElideRight
- color: Nheko.colors.text
+ color: palette.text
text: qsTr("Last seen %1 from %2").arg(new Date(lastTs).toLocaleString(Locale.ShortFormat)).arg(lastIp ? lastIp : "???")
}
@@ -504,7 +496,7 @@ ApplicationWindow {
ElidedLabel {
Layout.alignment: Qt.AlignVCenter
- color: Nheko.colors.text
+ color: palette.text
Layout.fillWidth: true
elideWidth: width
fullText: roomName
@@ -527,7 +519,7 @@ ApplicationWindow {
background: Rectangle {
anchors.fill: parent
- color: Nheko.colors.window
+ color: palette.window
}
}
diff --git a/resources/qml/emoji/StickerPicker.qml b/resources/qml/emoji/StickerPicker.qml
index 38788899..b7721db6 100644
--- a/resources/qml/emoji/StickerPicker.qml
+++ b/resources/qml/emoji/StickerPicker.qml
@@ -3,25 +3,22 @@
// SPDX-License-Identifier: GPL-3.0-or-later
import "../"
-import QtGraphicalEffects 1.0
-import QtQuick 2.9
-import QtQuick.Controls 2.3
-import QtQuick.Layouts 1.3
-import im.nheko 1.0
-import im.nheko.EmojiModel 1.0
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import im.nheko
Menu {
id: stickerPopup
property var callback
- property var colors
property string roomid
property alias model: gridView.model
required property bool emoji
property var textArea
- property real highlightHue: Nheko.colors.highlight.hslHue
- property real highlightSat: Nheko.colors.highlight.hslSaturation
- property real highlightLight: Nheko.colors.highlight.hslLightness
+ property real highlightHue: palette.highlight.hslHue
+ property real highlightSat: palette.highlight.hslSaturation
+ property real highlightLight: palette.highlight.hslLightness
readonly property int stickerDim: emoji ? 48 : 128
readonly property int stickerDimPad: stickerDim + Nheko.paddingSmall
readonly property int stickersPerRow: emoji ? 7 : 3
@@ -45,7 +42,7 @@ Menu {
width: sidebarAvatarSize + Nheko.paddingSmall + stickersPerRow * stickerDimPad + 20
Rectangle {
- color: Nheko.colors.window
+ color: palette.window
height: columnView.implicitHeight + Nheko.paddingSmall*2
width: sidebarAvatarSize + Nheko.paddingSmall + stickersPerRow * stickerDimPad + 20
@@ -67,10 +64,8 @@ Menu {
Layout.preferredWidth: stickersPerRow * stickerDimPad + 20 - Nheko.paddingSmall
Layout.row: 0
Layout.column: 1
- palette: Nheko.colors
background: null
- placeholderTextColor: Nheko.colors.buttonText
- color: Nheko.colors.text
+ placeholderTextColor: palette.buttonText
placeholderText: qsTr("Search")
selectByMouse: true
rightPadding: clearSearch.width
@@ -126,7 +121,7 @@ Menu {
section.delegate: Rectangle {
width: gridView.width
height: childrenRect.height
- color: Nheko.colors.alternateBase
+ color: palette.alternateBase
required property string section
@@ -134,7 +129,6 @@ Menu {
anchors.left: parent.left
anchors.right: parent.right
text: parent.section
- color: Nheko.colors.text
font.bold: true
}
}
@@ -142,12 +136,6 @@ Menu {
spacing: Nheko.paddingSmall
- ScrollHelper {
- flickable: parent
- anchors.fill: parent
- enabled: !Settings.mobileMode
- }
-
// Individual emoji
delegate: Row {
required property var row;
@@ -197,7 +185,6 @@ Menu {
font.family: Settings.emojiFont
font.pixelSize: 36
text: del.modelData.unicode.replace('\ufe0f', '')
- color: Nheko.colors.text
}
}
@@ -214,7 +201,7 @@ Menu {
background: Rectangle {
anchors.fill: parent
- color: hovered ? Nheko.colors.highlight : 'transparent'
+ color: hovered ? palette.highlight : 'transparent'
radius: 5
}
@@ -243,6 +230,7 @@ Menu {
height: sidebarAvatarSize
width: sidebarAvatarSize
url: modelData.url.replace("mxc://", "image://MxcImage/")
+ textColor: modelData.url.startsWith("mxc://") ? palette.text : palette.buttonText
displayName: modelData.name
roomid: modelData.name
diff --git a/resources/qml/pages/LoginPage.qml b/resources/qml/pages/LoginPage.qml
index 4273617f..9bf4e97e 100644
--- a/resources/qml/pages/LoginPage.qml
+++ b/resources/qml/pages/LoginPage.qml
@@ -25,7 +25,6 @@ Item {
id: scroll
clip: false
- palette: Nheko.colors
ScrollBar.horizontal.visible: false
anchors.left: parent.left
anchors.right: parent.right
@@ -71,7 +70,7 @@ Item {
visible: running
running: login.lookingUpHs
- foreground: Nheko.colors.mid
+ foreground: palette.mid
}
}
@@ -127,7 +126,7 @@ Item {
visible: running
running: login.loggingIn
- foreground: Nheko.colors.mid
+ foreground: palette.mid
}
}
diff --git a/resources/qml/pages/RegisterPage.qml b/resources/qml/pages/RegisterPage.qml
index 8536a254..c1bc5310 100644
--- a/resources/qml/pages/RegisterPage.qml
+++ b/resources/qml/pages/RegisterPage.qml
@@ -25,7 +25,6 @@ Item {
id: scroll
clip: false
- palette: Nheko.colors
ScrollBar.horizontal.visible: false
anchors.left: parent.left
anchors.right: parent.right
@@ -70,7 +69,7 @@ Item {
visible: running
running: regis.lookingUpHs
- foreground: Nheko.colors.mid
+ foreground: palette.mid
}
}
@@ -102,7 +101,7 @@ Item {
visible: running
running: regis.lookingUpUsername
- foreground: Nheko.colors.mid
+ foreground: palette.mid
}
Image {
@@ -178,7 +177,7 @@ Item {
visible: running
running: regis.registering
- foreground: Nheko.colors.mid
+ foreground: palette.mid
}
}
diff --git a/resources/qml/pages/UserSettingsPage.qml b/resources/qml/pages/UserSettingsPage.qml
index 5c2ebf5f..f23095b6 100644
--- a/resources/qml/pages/UserSettingsPage.qml
+++ b/resources/qml/pages/UserSettingsPage.qml
@@ -2,6 +2,7 @@
//
// SPDX-License-Identifier: GPL-3.0-or-later
+pragma ComponentBehavior: Bound
import ".."
import "../ui"
import Qt.labs.platform 1.1 as Platform
@@ -16,12 +17,11 @@ Rectangle {
property int collapsePoint: 600
property bool collapsed: width < collapsePoint
- color: Nheko.colors.window
+ color: palette.window
ScrollView {
id: scroll
- palette: Nheko.colors
ScrollBar.horizontal.visible: false
anchors.fill: parent
anchors.topMargin: (collapsed? backButton.height : 0)+Nheko.paddingLarge
@@ -34,15 +34,17 @@ Rectangle {
spacing: Nheko.paddingMedium
+ width: scroll.availableWidth
anchors.fill: parent
anchors.leftMargin: userSettingsDialog.collapsed ? 0 : (userSettingsDialog.width-userSettingsDialog.collapsePoint) * 0.4 + Nheko.paddingLarge
anchors.rightMargin: anchors.leftMargin
+
Repeater {
model: UserSettingsModel
- Layout.fillWidth:true
delegate: GridLayout {
+ width: scroll.availableWidth
columns: collapsed? 1 : 2
rows: collapsed? 2: 1
required property var model
@@ -51,7 +53,7 @@ Rectangle {
Label {
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
- color: Nheko.colors.text
+ color: palette.text
text: model.name
//Layout.column: 0
Layout.columnSpan: (model.type == UserSettingsModel.SectionTitle && !userSettingsDialog.collapsed) ? 2 : 1
@@ -79,7 +81,7 @@ Rectangle {
Layout.columnSpan: (model.type == UserSettingsModel.SectionTitle && !userSettingsDialog.collapsed) ? 2 : 1
Layout.preferredHeight: child.height
- Layout.preferredWidth: Math.min(child.implicitWidth, child.width || 1000)
+ Layout.preferredWidth: child.implicitWidth
Layout.maximumWidth: model.type == UserSettingsModel.SectionTitle ? Number.POSITIVE_INFINITY : 400
Layout.fillWidth: model.type == UserSettingsModel.SectionTitle || model.type == UserSettingsModel.Options || model.type == UserSettingsModel.Number
Layout.rightMargin: model.type == UserSettingsModel.SectionTitle ? 0 : Nheko.paddingMedium
@@ -96,10 +98,11 @@ Rectangle {
roleValue: UserSettingsModel.Options
ComboBox {
anchors.right: parent.right
- width: Math.min(parent.width, implicitWidth)
model: r.model.values
currentIndex: r.model.value
+ width: Math.min(implicitWidth, scroll.availableWidth - Nheko.paddingMedium)
onCurrentIndexChanged: r.model.value = currentIndex
+ implicitContentWidthPolicy: ComboBox.WidestTextWhenCompleted
WheelHandler{} // suppress scrolling changing values
}
@@ -109,7 +112,6 @@ Rectangle {
SpinBox {
anchors.right: parent.right
- width: Math.min(parent.width, implicitWidth)
from: model.valueLowerBound
to: model.valueUpperBound
stepSize: model.valueStep
@@ -130,7 +132,6 @@ Rectangle {
readonly property int decimals: 2
anchors.right: parent.right
- width: Math.min(parent.width, implicitWidth)
from: model.valueLowerBound * div
to: model.valueUpperBound * div
stepSize: model.valueStep * div
@@ -159,7 +160,7 @@ Rectangle {
DelegateChoice {
roleValue: UserSettingsModel.ReadOnlyText
TextEdit {
- color: Nheko.colors.text
+ color: palette.text
text: model.value
readOnly: true
selectByMouse: !Settings.mobileMode
@@ -176,7 +177,7 @@ Rectangle {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
- color: Nheko.colors.buttonText
+ color: palette.buttonText
height: 1
}
}
diff --git a/resources/qml/pages/WelcomePage.qml b/resources/qml/pages/WelcomePage.qml
index 6555cc29..3acdc18f 100644
--- a/resources/qml/pages/WelcomePage.qml
+++ b/resources/qml/pages/WelcomePage.qml
@@ -28,7 +28,7 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
text: qsTr("Welcome to nheko! The desktop client for the Matrix protocol.")
- color: Nheko.colors.text
+ color: palette.text
font.pointSize: fontMetrics.font.pointSize*2
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
@@ -38,7 +38,7 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
text: qsTr("Enjoy your stay!")
- color: Nheko.colors.text
+ color: palette.text
font.pointSize: fontMetrics.font.pointSize*1.5
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
@@ -86,7 +86,7 @@ ColumnLayout {
Layout.alignment: Qt.AlignLeft
Layout.margins: Nheko.paddingLarge
text: qsTr("Reduce animations")
- color: Nheko.colors.text
+ color: palette.text
HoverHandler {
id: hovered
diff --git a/resources/qml/ui/NhekoSlider.qml b/resources/qml/ui/NhekoSlider.qml
index 724e6e48..5e3a77d8 100644
--- a/resources/qml/ui/NhekoSlider.qml
+++ b/resources/qml/ui/NhekoSlider.qml
@@ -9,7 +9,7 @@ import im.nheko 1.0
Slider {
id: control
- property color progressColor: Nheko.colors.highlight
+ property color progressColor: palette.highlight
property bool alwaysShowSlider: true
property int sliderRadius: 16
@@ -25,7 +25,7 @@ Slider {
width: control.availableWidth - handle.width
height: implicitHeight
radius: height / 2
- color: Nheko.colors.buttonText
+ color: palette.buttonText
Rectangle {
width: control.visualPosition * parent.width
diff --git a/resources/qml/ui/Ripple.qml b/resources/qml/ui/Ripple.qml
index 192909b2..911b88cf 100644
--- a/resources/qml/ui/Ripple.qml
+++ b/resources/qml/ui/Ripple.qml
@@ -2,9 +2,8 @@
//
// SPDX-License-Identifier: GPL-3.0-or-later
-import QtGraphicalEffects 1.0
-import QtQuick 2.15
-import QtQuick.Controls 2.15
+import QtQuick
+import QtQuick.Controls
Item {
id: ripple
@@ -21,7 +20,7 @@ Item {
PointHandler {
id: ph
- onGrabChanged: {
+ onGrabChanged: (_, point) => {
circle.centerX = point.position.x
circle.centerY = point.position.y
}
diff --git a/resources/qml/ui/Snackbar.qml b/resources/qml/ui/Snackbar.qml
index 051db70d..0d334079 100644
--- a/resources/qml/ui/Snackbar.qml
+++ b/resources/qml/ui/Snackbar.qml
@@ -9,6 +9,9 @@ import im.nheko 1.0
Popup {
id: snackbar
+ // Workaround palettes not inheriting for popups
+ palette: timelineRoot.palette
+
property var messages: []
property string currentMessage: ""
@@ -45,7 +48,7 @@ Popup {
padding: Nheko.paddingLarge
contentItem: Label {
- color: Nheko.colors.light
+ color: palette.light
width: Math.max(Overlay.overlay? Overlay.overlay.width/2 : 0, 400)
text: snackbar.currentMessage
font.bold: true
@@ -53,7 +56,7 @@ Popup {
background: Rectangle {
radius: Nheko.paddingLarge
- color: Nheko.colors.dark
+ color: palette.dark
opacity: 0.8
}
diff --git a/resources/qml/ui/Spinner.qml b/resources/qml/ui/Spinner.qml
index a88318c6..9c0c8a31 100644
--- a/resources/qml/ui/Spinner.qml
+++ b/resources/qml/ui/Spinner.qml
@@ -3,8 +3,8 @@
// SPDX-License-Identifier: GPL-3.0-or-later
import "./animations"
-import QtGraphicalEffects 1.12
-import QtQuick 2.12
+import QtQuick
+import QtQuick.Effects
Item {
id: spinner
@@ -139,11 +139,11 @@ Item {
}
- Glow {
+ MultiEffect {
anchors.fill: row
- radius: 14
- samples: 17
- color: spinner.foreground
+ shadowBlur: 14
+ shadowEnabled: true
+ shadowColor: spinner.foreground
source: row
transform: Matrix4x4 {
diff --git a/resources/qml/ui/TimelineEffects.qml b/resources/qml/ui/TimelineEffects.qml
index aaff04a0..72237e31 100644
--- a/resources/qml/ui/TimelineEffects.qml
+++ b/resources/qml/ui/TimelineEffects.qml
@@ -6,34 +6,36 @@ import QtQuick 2.15
import QtQuick.Particles 2.15
Item {
+ id: effectRoot
readonly property int maxLifespan: Math.max(confettiEmitter.lifeSpan, rainfallEmitter.lifeSpan)
+ required property bool shouldEffectsRun
function pulseConfetti()
{
- confettiEmitter.pulse(parent.height * 2)
+ confettiEmitter.pulse(effectRoot.height * 2)
}
function pulseRainfall()
{
- rainfallEmitter.pulse(parent.height * 3.3)
+ rainfallEmitter.pulse(effectRoot.height * 3.3)
}
ParticleSystem {
id: particleSystem
Component.onCompleted: pause();
- paused: !shouldEffectsRun
+ paused: !effectRoot.shouldEffectsRun
}
Emitter {
id: confettiEmitter
group: "confetti"
- width: parent.width * 3/4
+ width: effectRoot.width * 3/4
enabled: false
- anchors.horizontalCenter: parent.horizontalCenter
- y: parent.height
- emitRate: Math.min(400 * Math.sqrt(parent.width * parent.height) / 870, 1000)
+ anchors.horizontalCenter: effectRoot.horizontalCenter
+ y: effectRoot.height
+ emitRate: Math.min(400 * Math.sqrt(effectRoot.width * effectRoot.height) / 870, 1000)
lifeSpan: 15000
system: particleSystem
maximumEmitted: 500
@@ -42,8 +44,8 @@ Item {
sizeVariation: 4
velocity: PointDirection {
x: 0
- y: -Math.min(450 * parent.height / 700, 1000)
- xVariation: Math.min(4 * parent.width / 7, 450)
+ y: -Math.min(450 * effectRoot.height / 700, 1000)
+ xVariation: Math.min(4 * effectRoot.width / 7, 450)
yVariation: 250
}
}
@@ -74,7 +76,7 @@ Item {
Gravity {
system: particleSystem
groups: ["confetti"]
- anchors.fill: parent
+ anchors.fill: effectRoot
magnitude: 350
angle: 90
}
@@ -83,11 +85,11 @@ Item {
id: rainfallEmitter
group: "rain"
- width: parent.width
+ width: effectRoot.width
enabled: false
- anchors.horizontalCenter: parent.horizontalCenter
+ anchors.horizontalCenter: effectRoot.horizontalCenter
y: -60
- emitRate: parent.width / 50
+ emitRate: effectRoot.width / 50
lifeSpan: 10000
system: particleSystem
velocity: PointDirection {
diff --git a/resources/qml/ui/animations/BlinkAnimation.qml b/resources/qml/ui/animations/BlinkAnimation.qml
index ae730452..de2a11d8 100644
--- a/resources/qml/ui/animations/BlinkAnimation.qml
+++ b/resources/qml/ui/animations/BlinkAnimation.qml
@@ -2,8 +2,7 @@
//
// SPDX-License-Identifier: GPL-3.0-or-later
-import QtGraphicalEffects 1.12
-import QtQuick 2.12
+import QtQuick
SequentialAnimation {
property alias target: numberAnimation.target
diff --git a/resources/qml/ui/animations/qmldir b/resources/qml/ui/animations/qmldir
deleted file mode 100644
index 14f9ad86..00000000
--- a/resources/qml/ui/animations/qmldir
+++ /dev/null
@@ -1,2 +0,0 @@
-module im.nheko.UI.Animations
-BlinkAnimation 1.0 BlinkAnimation.qml
diff --git a/resources/qml/ui/media/MediaControls.qml b/resources/qml/ui/media/MediaControls.qml
index f1cb7ca1..bd5f6ddc 100644
--- a/resources/qml/ui/media/MediaControls.qml
+++ b/resources/qml/ui/media/MediaControls.qml
@@ -4,11 +4,11 @@
import "../"
import "../../"
-import QtMultimedia 5.15
-import QtQuick 2.15
-import QtQuick.Controls 2.15
-import QtQuick.Layouts 1.15
-import im.nheko 1.0
+import QtMultimedia
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import im.nheko
Rectangle {
id: control
@@ -50,7 +50,7 @@ Rectangle {
}
color: {
- var wc = Nheko.colors.alternateBase;
+ var wc = palette.alternateBase;
return Qt.rgba(wc.r, wc.g, wc.b, 0.5);
}
opacity: control.shouldShowControls ? 1 : 0
@@ -95,7 +95,7 @@ Rectangle {
id: playbackStateImage
Layout.alignment: Qt.AlignLeft
- buttonTextColor: Nheko.colors.text
+ buttonTextColor: palette.text
Layout.preferredHeight: 24
Layout.preferredWidth: 24
image: {
@@ -115,7 +115,7 @@ Rectangle {
id: volumeButton
Layout.alignment: Qt.AlignLeft
- buttonTextColor: Nheko.colors.text
+ buttonTextColor: palette.text
Layout.preferredHeight: 24
Layout.preferredWidth: 24
image: {
@@ -130,7 +130,7 @@ Rectangle {
NhekoSlider {
id: volumeSlider
- property real desiredVolume: QtMultimedia.convertVolume(volumeSlider.value, QtMultimedia.LogarithmicVolumeScale, QtMultimedia.LinearVolumeScale)
+ property real desiredVolume: volumeSlider.value
state: ""
Layout.alignment: Qt.AlignLeft
@@ -214,7 +214,7 @@ Rectangle {
Label {
Layout.alignment: Qt.AlignRight
text: (!control.mediaLoaded ? "-- " : durationToString(control.positionValue)) + " / " + durationToString(control.duration)
- color: Nheko.colors.text
+ color: palette.text
}
Item {
diff --git a/resources/qml/ui/media/qmldir b/resources/qml/ui/media/qmldir
deleted file mode 100644
index 143b603d..00000000
--- a/resources/qml/ui/media/qmldir
+++ /dev/null
@@ -1,3 +0,0 @@
-module im.nheko.UI.Media
-VolumeSlider 1.0 VolumeSlider.qml
-MediaControls 1.0 MediaControls.qml
\ No newline at end of file
diff --git a/resources/qml/ui/qmldir b/resources/qml/ui/qmldir
deleted file mode 100644
index a2ce7514..00000000
--- a/resources/qml/ui/qmldir
+++ /dev/null
@@ -1,4 +0,0 @@
-module im.nheko.UI
-NhekoSlider 1.0 NhekoSlider.qml
-Ripple 1.0 Ripple.qml
-Spinner 1.0 Spinner.qml
\ No newline at end of file
diff --git a/resources/qml/voip/ActiveCallBar.qml b/resources/qml/voip/ActiveCallBar.qml
index d055c013..45afedab 100644
--- a/resources/qml/voip/ActiveCallBar.qml
+++ b/resources/qml/voip/ActiveCallBar.qml
@@ -16,7 +16,7 @@ Rectangle {
MouseArea {
anchors.fill: parent
onClicked: {
- if (CallManager.callType != CallType.VOICE)
+ if (CallManager.callType != Voip.VOICE)
stackLayout.currentIndex = stackLayout.currentIndex ? 0 : 1;
}
@@ -58,7 +58,7 @@ Rectangle {
states: [
State {
name: "VOICE"
- when: CallManager.callType == CallType.VOICE
+ when: CallManager.callType == Voip.VOICE
PropertyChanges {
target: callTypeIcon
@@ -68,7 +68,7 @@ Rectangle {
},
State {
name: "VIDEO"
- when: CallManager.callType == CallType.VIDEO
+ when: CallManager.callType == Voip.VIDEO
PropertyChanges {
target: callTypeIcon
@@ -78,7 +78,7 @@ Rectangle {
},
State {
name: "SCREEN"
- when: CallManager.callType == CallType.SCREEN
+ when: CallManager.callType == Voip.SCREEN
PropertyChanges {
target: callTypeIcon
@@ -100,7 +100,7 @@ Rectangle {
states: [
State {
name: "OFFERSENT"
- when: CallManager.callState == WebRTCState.OFFERSENT
+ when: CallManager.callState == Voip.OFFERSENT
PropertyChanges {
target: callStateLabel
@@ -110,7 +110,7 @@ Rectangle {
},
State {
name: "CONNECTING"
- when: CallManager.callState == WebRTCState.CONNECTING
+ when: CallManager.callState == Voip.CONNECTING
PropertyChanges {
target: callStateLabel
@@ -120,7 +120,7 @@ Rectangle {
},
State {
name: "ANSWERSENT"
- when: CallManager.callState == WebRTCState.ANSWERSENT
+ when: CallManager.callState == Voip.ANSWERSENT
PropertyChanges {
target: callStateLabel
@@ -130,7 +130,7 @@ Rectangle {
},
State {
name: "CONNECTED"
- when: CallManager.callState == WebRTCState.CONNECTED
+ when: CallManager.callState == Voip.CONNECTED
PropertyChanges {
target: callStateLabel
@@ -144,13 +144,13 @@ Rectangle {
PropertyChanges {
target: stackLayout
- currentIndex: CallManager.callType != CallType.VOICE ? 1 : 0
+ currentIndex: CallManager.callType != Voip.VOICE ? 1 : 0
}
},
State {
name: "DISCONNECTED"
- when: CallManager.callState == WebRTCState.DISCONNECTED
+ when: CallManager.callState == Voip.DISCONNECTED
PropertyChanges {
target: callStateLabel
@@ -176,7 +176,7 @@ Rectangle {
}
interval: 1000
- running: CallManager.callState == WebRTCState.CONNECTED
+ running: CallManager.callState == Voip.CONNECTED
repeat: true
onTriggered: {
var d = new Date();
@@ -190,7 +190,7 @@ Rectangle {
Label {
Layout.leftMargin: 16
- visible: CallManager.callType == CallType.SCREEN && CallManager.callState == WebRTCState.CONNECTED
+ visible: CallManager.callType == Voip.SCREEN && CallManager.callState == Voip.CONNECTED
text: qsTr("You are screen sharing")
font.pointSize: fontMetrics.font.pointSize * 1.1
color: "#000000"
diff --git a/resources/qml/voip/CallDevices.qml b/resources/qml/voip/CallDevices.qml
index 5a1792c9..46c8cde3 100644
--- a/resources/qml/voip/CallDevices.qml
+++ b/resources/qml/voip/CallDevices.qml
@@ -9,7 +9,6 @@ import im.nheko 1.0
Popup {
modal: true
- palette: Nheko.colors
// only set the anchors on Qt 5.12 or higher
// see https://doc.qt.io/qt-5/qml-qtquick-controls2-popup.html#anchors.centerIn-prop
Component.onCompleted: {
@@ -31,7 +30,7 @@ Popup {
Image {
Layout.preferredWidth: 22
Layout.preferredHeight: 22
- source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + Nheko.colors.windowText
+ source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + palette.windowText
}
ComboBox {
@@ -44,12 +43,12 @@ Popup {
}
RowLayout {
- visible: CallManager.callType == CallType.VIDEO && CallManager.cameras.length > 0
+ visible: CallManager.callType == Voip.VIDEO && CallManager.cameras.length > 0
Image {
Layout.preferredWidth: 22
Layout.preferredHeight: 22
- source: "image://colorimage/:/icons/icons/ui/video-call.svg?" + Nheko.colors.windowText
+ source: "image://colorimage/:/icons/icons/ui/video-call.svg?" + palette.windowText
}
ComboBox {
@@ -81,8 +80,8 @@ Popup {
}
background: Rectangle {
- color: Nheko.colors.window
- border.color: Nheko.colors.windowText
+ color: palette.window
+ border.color: palette.windowText
}
}
diff --git a/resources/qml/voip/CallInvite.qml b/resources/qml/voip/CallInvite.qml
index 8ffe9892..8a609c32 100644
--- a/resources/qml/voip/CallInvite.qml
+++ b/resources/qml/voip/CallInvite.qml
@@ -14,7 +14,6 @@ Popup {
closePolicy: Popup.NoAutoClose
width: parent.width
height: parent.height
- palette: Nheko.colors
Component {
id: deviceError
@@ -45,7 +44,7 @@ Popup {
Layout.fillWidth: true
text: CallManager.callPartyDisplayName
font.pointSize: fontMetrics.font.pointSize * 2
- color: Nheko.colors.windowText
+ color: palette.windowText
horizontalAlignment: Text.AlignHCenter
}
@@ -63,19 +62,19 @@ Popup {
Layout.bottomMargin: callInv.height / 25
Image {
- property string image: CallManager.callType == CallType.VIDEO ? ":/icons/icons/ui/video.svg" : ":/icons/icons/ui/place-call.svg"
+ property string image: CallManager.callType == Voip.VIDEO ? ":/icons/icons/ui/video.svg" : ":/icons/icons/ui/place-call.svg"
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: callInv.height / 10
Layout.preferredHeight: callInv.height / 10
- source: "image://colorimage/" + image + "?" + Nheko.colors.windowText
+ source: "image://colorimage/" + image + "?" + palette.windowText
}
Label {
Layout.alignment: Qt.AlignCenter
- text: CallManager.callType == CallType.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
+ text: CallManager.callType == Voip.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
font.pointSize: fontMetrics.font.pointSize * 2
- color: Nheko.colors.windowText
+ color: palette.windowText
}
}
@@ -94,7 +93,7 @@ Popup {
Image {
Layout.preferredWidth: deviceCombos.imageSize
Layout.preferredHeight: deviceCombos.imageSize
- source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + Nheko.colors.windowText
+ source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + palette.windowText
}
ComboBox {
@@ -107,13 +106,13 @@ Popup {
}
RowLayout {
- visible: CallManager.callType == CallType.VIDEO && CallManager.cameras.length > 0
+ visible: CallManager.callType == Voip.VIDEO && CallManager.cameras.length > 0
Layout.alignment: Qt.AlignCenter
Image {
Layout.preferredWidth: deviceCombos.imageSize
Layout.preferredHeight: deviceCombos.imageSize
- source: "image://colorimage/:/icons/icons/ui/video.svg?" + Nheko.colors.windowText
+ source: "image://colorimage/:/icons/icons/ui/video.svg?" + palette.windowText
}
ComboBox {
@@ -170,7 +169,7 @@ Popup {
RoundButton {
id: acceptButton
- property string image: CallManager.callType == CallType.VIDEO ? ":/icons/icons/ui/video.svg" : ":/icons/icons/ui/place-call.svg"
+ property string image: CallManager.callType == Voip.VIDEO ? ":/icons/icons/ui/video.svg" : ":/icons/icons/ui/place-call.svg"
implicitWidth: buttonLayout.buttonSize
implicitHeight: buttonLayout.buttonSize
@@ -201,8 +200,8 @@ Popup {
}
background: Rectangle {
- color: Nheko.colors.window
- border.color: Nheko.colors.windowText
+ color: palette.window
+ border.color: palette.windowText
}
}
diff --git a/resources/qml/voip/CallInviteBar.qml b/resources/qml/voip/CallInviteBar.qml
index 4eaff020..d82bd143 100644
--- a/resources/qml/voip/CallInviteBar.qml
+++ b/resources/qml/voip/CallInviteBar.qml
@@ -57,12 +57,12 @@ Rectangle {
Layout.leftMargin: 4
Layout.preferredWidth: 24
Layout.preferredHeight: 24
- source: CallManager.callType == CallType.VIDEO ? "qrc:/icons/icons/ui/video.svg" : "qrc:/icons/icons/ui/place-call.svg"
+ source: CallManager.callType == Voip.VIDEO ? "qrc:/icons/icons/ui/video.svg" : "qrc:/icons/icons/ui/place-call.svg"
}
Label {
font.pointSize: fontMetrics.font.pointSize * 1.1
- text: CallManager.callType == CallType.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
+ text: CallManager.callType == Voip.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
color: "#000000"
}
@@ -88,9 +88,8 @@ Rectangle {
Button {
Layout.rightMargin: 4
- icon.source: CallManager.callType == CallType.VIDEO ? "qrc:/icons/icons/ui/video.svg" : "qrc:/icons/icons/ui/place-call.svg"
+ icon.source: CallManager.callType == Voip.VIDEO ? "qrc:/icons/icons/ui/video.svg" : "qrc:/icons/icons/ui/place-call.svg"
text: qsTr("Accept")
- palette: Nheko.colors
onClicked: {
if (CallManager.mics.length == 0) {
var dialog = deviceError.createObject(timelineRoot, {
@@ -109,7 +108,7 @@ Rectangle {
timelineRoot.destroyOnClose(dialog);
return ;
}
- if (CallManager.callType == CallType.VIDEO && CallManager.cameras.length > 0 && !CallManager.cameras.includes(Settings.camera)) {
+ if (CallManager.callType == Voip.VIDEO && CallManager.cameras.length > 0 && !CallManager.cameras.includes(Settings.camera)) {
var dialog = deviceError.createObject(timelineRoot, {
"errorString": qsTr("Unknown camera: %1").arg(Settings.camera),
"image": ":/icons/icons/ui/video.svg"
@@ -126,7 +125,6 @@ Rectangle {
Layout.rightMargin: 16
icon.source: "qrc:/icons/icons/ui/end-call.svg"
text: qsTr("Decline")
- palette: Nheko.colors
onClicked: {
CallManager.rejectInvite();
}
diff --git a/resources/qml/voip/DeviceError.qml b/resources/qml/voip/DeviceError.qml
index 9328e385..4b9b4675 100644
--- a/resources/qml/voip/DeviceError.qml
+++ b/resources/qml/voip/DeviceError.qml
@@ -5,9 +5,9 @@
import QtQuick 2.9
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
-import im.nheko 1.0
Popup {
+ id: r
property string errorString
property var image
@@ -24,19 +24,19 @@ Popup {
Image {
Layout.preferredWidth: 16
Layout.preferredHeight: 16
- source: "image://colorimage/" + image + "?" + Nheko.colors.windowText
+ source: "image://colorimage/" + r.image + "?" + palette.windowText
}
Label {
- text: errorString
- color: Nheko.colors.windowText
+ text: r.errorString
+ color: palette.windowText
}
}
background: Rectangle {
- color: Nheko.colors.window
- border.color: Nheko.colors.windowText
+ color: palette.window
+ border.color: palette.windowText
}
}
diff --git a/resources/qml/voip/PlaceCall.qml b/resources/qml/voip/PlaceCall.qml
index c7a64342..c0724828 100644
--- a/resources/qml/voip/PlaceCall.qml
+++ b/resources/qml/voip/PlaceCall.qml
@@ -17,7 +17,6 @@ Popup {
anchors.centerIn = parent;
}
- palette: Nheko.colors
Component {
id: deviceError
@@ -38,7 +37,7 @@ Popup {
Label {
text: qsTr("Place a call to %1?").arg(room.roomName)
- color: Nheko.colors.windowText
+ color: palette.windowText
}
Item {
@@ -68,8 +67,8 @@ Popup {
Avatar {
Layout.rightMargin: cameraCombo.visible ? 16 : 64
- width: Nheko.avatarSize
- height: Nheko.avatarSize
+ Layout.preferredWidth: Nheko.avatarSize
+ Layout.preferredHeight: Nheko.avatarSize
url: room.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
displayName: room.roomName
roomid: room.roomId
@@ -82,7 +81,7 @@ Popup {
onClicked: {
if (buttonLayout.validateMic()) {
Settings.microphone = micCombo.currentText;
- CallManager.sendInvite(room.roomId, CallType.VOICE);
+ CallManager.sendInvite(room.roomId, Voip.VOICE);
close();
}
}
@@ -96,7 +95,7 @@ Popup {
if (buttonLayout.validateMic()) {
Settings.microphone = micCombo.currentText;
Settings.camera = cameraCombo.currentText;
- CallManager.sendInvite(room.roomId, CallType.VIDEO);
+ CallManager.sendInvite(room.roomId, Voip.VIDEO);
close();
}
}
@@ -138,7 +137,7 @@ Popup {
Image {
Layout.preferredWidth: 22
Layout.preferredHeight: 22
- source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + Nheko.colors.windowText
+ source: "image://colorimage/:/icons/icons/ui/microphone-unmute.svg?" + palette.windowText
}
ComboBox {
@@ -159,7 +158,7 @@ Popup {
Image {
Layout.preferredWidth: 22
Layout.preferredHeight: 22
- source: "image://colorimage/:/icons/icons/ui/video.svg?" + Nheko.colors.windowText
+ source: "image://colorimage/:/icons/icons/ui/video.svg?" + palette.windowText
}
ComboBox {
@@ -176,8 +175,8 @@ Popup {
}
background: Rectangle {
- color: Nheko.colors.window
- border.color: Nheko.colors.windowText
+ color: palette.window
+ border.color: palette.windowText
}
}
diff --git a/resources/qml/voip/ScreenShare.qml b/resources/qml/voip/ScreenShare.qml
index 1a82a5ce..7f8665bc 100644
--- a/resources/qml/voip/ScreenShare.qml
+++ b/resources/qml/voip/ScreenShare.qml
@@ -19,7 +19,6 @@ Popup {
Component.onDestruction: {
CallManager.closeScreenShare();
}
- palette: Nheko.colors
ColumnLayout {
Label {
@@ -29,7 +28,7 @@ Popup {
Layout.rightMargin: 8
Layout.alignment: Qt.AlignLeft
text: qsTr("Share desktop with %1?").arg(room.roomName)
- color: Nheko.colors.windowText
+ color: palette.windowText
}
RowLayout {
@@ -40,7 +39,7 @@ Popup {
Label {
Layout.alignment: Qt.AlignLeft
text: qsTr("Method:")
- color: Nheko.colors.windowText
+ color: palette.windowText
}
ComboBox {
@@ -60,11 +59,11 @@ Popup {
Label {
Layout.alignment: Qt.AlignLeft
text: qsTr("Window:")
- color: Nheko.colors.windowText
+ color: palette.windowText
}
ComboBox {
- visible: CallManager.screenShareType == ScreenShareType.X11
+ visible: CallManager.screenShareType == Voip.X11
id: windowCombo
Layout.fillWidth: true
@@ -72,7 +71,7 @@ Popup {
}
Button {
- visible: CallManager.screenShareType == ScreenShareType.XDP
+ visible: CallManager.screenShareType == Voip.XDP
highlighted: !CallManager.screenShareReady
text: qsTr("Request screencast")
onClicked: {
@@ -91,7 +90,7 @@ Popup {
Label {
Layout.alignment: Qt.AlignLeft
text: qsTr("Frame rate:")
- color: Nheko.colors.windowText
+ color: palette.windowText
}
ComboBox {
@@ -166,7 +165,7 @@ Popup {
Settings.screenShareRemoteVideo = remoteVideoCheckBox.checked;
Settings.screenShareHideCursor = hideCursorCheckBox.checked;
- CallManager.sendInvite(room.roomId, CallType.SCREEN, windowCombo.currentIndex);
+ CallManager.sendInvite(room.roomId, Voip.SCREEN, windowCombo.currentIndex);
close();
}
}
@@ -191,8 +190,8 @@ Popup {
}
background: Rectangle {
- color: Nheko.colors.window
- border.color: Nheko.colors.windowText
+ color: palette.window
+ border.color: palette.windowText
}
}
diff --git a/resources/res.qrc b/resources/res.qrc
index bcef8841..fb857d4a 100644
--- a/resources/res.qrc
+++ b/resources/res.qrc
@@ -90,115 +90,9 @@
nheko-32.png
nheko-16.png
-
- styles/system.qss
- styles/nheko.qss
- styles/nheko-dark.qss
-
qtquickcontrols2.conf
- qml/Root.qml
- qml/ChatPage.qml
- qml/CommunitiesList.qml
- qml/RoomList.qml
- qml/TimelineView.qml
- qml/Avatar.qml
- qml/Completer.qml
- qml/EncryptionIndicator.qml
- qml/ImageButton.qml
- qml/ElidedLabel.qml
- qml/MatrixText.qml
- qml/MatrixTextField.qml
- qml/ToggleButton.qml
- qml/UploadBox.qml
- qml/MessageInput.qml
- qml/MessageView.qml
- qml/PrivacyScreen.qml
- qml/Reactions.qml
- qml/ReplyPopup.qml
- qml/ScrollHelper.qml
- qml/StatusIndicator.qml
- qml/TimelineRow.qml
- qml/TopBar.qml
- qml/QuickSwitcher.qml
- qml/ForwardCompleter.qml
- qml/SelfVerificationCheck.qml
- qml/TypingIndicator.qml
- qml/MessageInputWarning.qml
- qml/components/AdaptiveLayout.qml
- qml/components/AdaptiveLayoutElement.qml
- qml/components/AvatarListTile.qml
- qml/components/FlatButton.qml
- qml/components/MainWindowDialog.qml
- qml/components/NhekoTabButton.qml
- qml/components/NotificationBubble.qml
- qml/components/ReorderableListview.qml
- qml/components/SpaceMenuLevel.qml
- qml/components/TextButton.qml
- qml/components/UserListRow.qml
- qml/delegates/Encrypted.qml
- qml/delegates/FileMessage.qml
- qml/delegates/ImageMessage.qml
- qml/delegates/MessageDelegate.qml
- qml/delegates/NoticeMessage.qml
- qml/delegates/Pill.qml
- qml/delegates/Placeholder.qml
- qml/delegates/PlayableMediaMessage.qml
- qml/delegates/Redacted.qml
- qml/delegates/Reply.qml
- qml/delegates/TextMessage.qml
- qml/device-verification/DeviceVerification.qml
- qml/device-verification/DigitVerification.qml
- qml/device-verification/EmojiVerification.qml
- qml/device-verification/Failed.qml
- qml/device-verification/NewVerificationRequest.qml
- qml/device-verification/Success.qml
- qml/device-verification/Waiting.qml
- qml/dialogs/AliasEditor.qml
- qml/dialogs/ConfirmJoinRoomDialog.qml
- qml/dialogs/CreateDirect.qml
- qml/dialogs/CreateRoom.qml
- qml/dialogs/HiddenEventsDialog.qml
- qml/dialogs/ImageOverlay.qml
- qml/dialogs/ImagePackEditorDialog.qml
- qml/dialogs/ImagePackSettingsDialog.qml
- qml/dialogs/InputDialog.qml
- qml/dialogs/InviteDialog.qml
- qml/dialogs/JoinRoomDialog.qml
- qml/dialogs/LeaveRoomDialog.qml
- qml/dialogs/LogoutDialog.qml
- qml/dialogs/PhoneNumberInputDialog.qml
- qml/dialogs/PowerLevelEditor.qml
- qml/dialogs/PowerLevelSpacesApplyDialog.qml
- qml/dialogs/RawMessageDialog.qml
- qml/dialogs/ReadReceipts.qml
- qml/dialogs/RoomDirectory.qml
- qml/dialogs/RoomMembers.qml
- qml/dialogs/AllowedRoomsSettingsDialog.qml
- qml/dialogs/RoomSettings.qml
- qml/dialogs/UserProfile.qml
- qml/emoji/StickerPicker.qml
- qml/pages/LoginPage.qml
- qml/pages/RegisterPage.qml
- qml/pages/UserSettingsPage.qml
- qml/pages/WelcomePage.qml
- qml/ui/NhekoSlider.qml
- qml/ui/Ripple.qml
- qml/ui/Snackbar.qml
- qml/ui/Spinner.qml
- qml/ui/animations/BlinkAnimation.qml
- qml/ui/media/MediaControls.qml
- qml/voip/ActiveCallBar.qml
- qml/voip/CallDevices.qml
- qml/voip/CallInvite.qml
- qml/voip/CallInviteBar.qml
- qml/voip/DeviceError.qml
- qml/voip/PlaceCall.qml
- qml/voip/ScreenShare.qml
- qml/voip/VideoCall.qml
confettiparticle.svg
- qml/delegates/EncryptionEnabled.qml
- qml/ui/TimelineEffects.qml
media/ring.ogg
diff --git a/resources/styles/nheko-dark.qss b/resources/styles/nheko-dark.qss
deleted file mode 100644
index 597397cd..00000000
--- a/resources/styles/nheko-dark.qss
+++ /dev/null
@@ -1,77 +0,0 @@
-TextLabel,
-QLabel {
- color: #caccd1;
-}
-
-TextLabel::a {
- color: #38a3d8;
-}
-
-QuickSwitcher,
-ReplyPopup,
-SuggestionsPopup,
-UserSettingsPage,
-#scroll_widget,
-#UserSettingScrollWidget {
- background-color: #202228;
-}
-
-QLineEdit,
-EditModal,
-dialogs--ReCaptcha,
-dialogs--JoinRoom {
- background-color: #202228;
- color: #caccd1;
-}
-
-PopupItem {
- background-color: #202228;
- qproperty-hoverColor: rgba(45, 49, 57, 120);
-}
-
-FlatButton {
- qproperty-foregroundColor: #727274;
- qproperty-backgroundColor: #333;
- qproperty-disabledForegroundColor: #222;
-}
-
-RaisedButton {
- qproperty-foregroundColor: #caccd1;
- qproperty-backgroundColor: #333;
-}
-
-FloatingButton {
- qproperty-backgroundColor: #2d3139;
- qproperty-foregroundColor: white;
-}
-
-TextField {
- qproperty-backgroundColor: #202228;
- qproperty-inkColor: #caccd1;
- qproperty-labelColor: #caccd1;
-}
-
-TextInputWidget {
- border: none;
-}
-
-TextInputWidget,
-TextInputWidget > QTextEdit,
-TextInputWidget > QLineEdit {
- background-color: #2d3139;
- color: #caccd1;
-}
-
-Toggle {
- qproperty-activeColor: #38a3d8;
- qproperty-disabledColor: gray;
- qproperty-inactiveColor: gray;
- qproperty-trackColor: rgb(240, 240, 240);
-}
-
-QListWidget {
- color: #caccd1;
- background-color: #202228;
-}
-
-QSplitter::handle { image: none; }
diff --git a/resources/styles/nheko.qss b/resources/styles/nheko.qss
deleted file mode 100644
index b4b7d427..00000000
--- a/resources/styles/nheko.qss
+++ /dev/null
@@ -1,68 +0,0 @@
-TextLabel,
-QLabel {
- color: #333;
-}
-
-TextLabel::a {
- color: #0077b5;
-}
-
-
-PopupItem {
- background-color: white;
- qproperty-hoverColor: rgba(192, 193, 195, 120);
-}
-
-FlatButton {
- qproperty-foregroundColor: #495057;
-}
-
-RaisedButton {
- qproperty-foregroundColor: white;
-}
-
-dialogs--ReCaptcha,
-dialogs--JoinRoom,
-EditModal,
-QListWidget {
- background-color: white;
- color: #495057;
-}
-
-QComboBox,
-QPushButton {
- background-color: white;
- color: #333;
-}
-
-FloatingButton {
- qproperty-backgroundColor: #efefef;
- qproperty-foregroundColor: black;
-}
-
-TextField {
- qproperty-backgroundColor: white;
- qproperty-inkColor: #333;
- qproperty-labelColor: #333;
-}
-
-QListWidget,
-TextInputWidget,
-QTextEdit,
-QLineEdit {
- background-color: white;
- color: #333;
-}
-
-TextInputWidget {
- border: none;
-}
-
-Toggle {
- qproperty-activeColor: #38a3d8;
- qproperty-disabledColor: gray;
- qproperty-inactiveColor: gray;
- qproperty-trackColor: rgb(240, 240, 240);
-}
-
-QSplitter::handle { image: none; }
diff --git a/resources/styles/system.qss b/resources/styles/system.qss
deleted file mode 100644
index d2305974..00000000
--- a/resources/styles/system.qss
+++ /dev/null
@@ -1,39 +0,0 @@
-TextInputWidget {
- border: none;
-}
-
-PopupItem {
- qproperty-hoverColor: palette(base);
-}
-
-FlatButton {
- qproperty-foregroundColor: palette(text);
-}
-
-RaisedButton {
- qproperty-foregroundColor: palette(button-text);
-}
-
-TextField {
- qproperty-backgroundColor: palette(window);
-}
-
-QTextEdit,
-QLineEdit,
-QListWidget {
- background-color: palette(window);
-}
-
-FloatingButton {
- qproperty-backgroundColor: palette(base);
- qproperty-foregroundColor: palette(text);
-}
-
-Toggle {
- qproperty-activeColor: palette(highlight);
- qproperty-disabledColor: palette(dark);
- qproperty-inactiveColor: palette(mid);
- qproperty-trackColor: palette(base);
-}
-
-QSplitter::handle { image: none; }
diff --git a/src/AliasEditModel.h b/src/AliasEditModel.h
index 2263659b..04de5016 100644
--- a/src/AliasEditModel.h
+++ b/src/AliasEditModel.h
@@ -5,6 +5,7 @@
#pragma once
#include
+#include
#include
#include
@@ -29,6 +30,9 @@ signals:
class AliasEditingModel final : public QAbstractListModel
{
Q_OBJECT
+ QML_ELEMENT
+ QML_UNCREATABLE("Please use editAliases to create the models")
+
Q_PROPERTY(bool canAdvertize READ canAdvertize CONSTANT)
public:
diff --git a/src/Cache.cpp b/src/Cache.cpp
index a2fecd3e..fc158e82 100644
--- a/src/Cache.cpp
+++ b/src/Cache.cpp
@@ -21,7 +21,7 @@
#if __has_include()
#include
#else
-#include
+#include
#endif
#include
@@ -94,11 +94,6 @@ static constexpr auto MEGOLM_SESSIONS_DATA_DB("megolm_sessions_data_db");
using CachedReceipts = std::multimap>;
using Receipts = std::map>;
-Q_DECLARE_METATYPE(RoomMember)
-Q_DECLARE_METATYPE(mtx::responses::Timeline)
-Q_DECLARE_METATYPE(RoomInfo)
-Q_DECLARE_METATYPE(mtx::responses::QueryKeys)
-
namespace {
std::unique_ptr instance_ = nullptr;
}
@@ -440,7 +435,7 @@ Cache::loadSecretsFromStore(
if (job->error() && job->error() != QKeychain::Error::EntryNotFound) {
nhlog::db()->error("Restoring secret '{}' failed ({}): {}",
name.toStdString(),
- job->error(),
+ static_cast(job->error()),
job->errorString().toStdString());
fatalSecretError();
@@ -2140,8 +2135,16 @@ Cache::saveInvite(lmdb::txn &txn,
auto display_name =
msg->content.display_name.empty() ? msg->state_key : msg->content.display_name;
- MemberInfo tmp{
- display_name, msg->content.avatar_url, msg->content.reason, msg->content.is_direct};
+ std::string inviter = "";
+ if (msg->content.membership == mtx::events::state::Membership::Invite) {
+ inviter = msg->sender;
+ }
+
+ MemberInfo tmp{display_name,
+ msg->content.avatar_url,
+ inviter,
+ msg->content.reason,
+ msg->content.is_direct};
membersdb.put(txn, msg->state_key, nlohmann::json(tmp).dump());
} else {
@@ -4436,7 +4439,9 @@ Cache::displayName(const QString &room_id, const QString &user_id)
static bool
isDisplaynameSafe(const std::string &s)
{
- for (QChar c : QString::fromStdString(s).toStdU32String()) {
+ const auto str = QString::fromStdString(s);
+
+ for (QChar c : str) {
if (c.isPrint() && !c.isSpace())
return false;
}
@@ -5183,6 +5188,8 @@ to_json(nlohmann::json &j, const MemberInfo &info)
{
j["name"] = info.name;
j["avatar_url"] = info.avatar_url;
+ if (!info.inviter.empty())
+ j["inviter"] = info.inviter;
if (info.is_direct)
j["is_direct"] = info.is_direct;
if (!info.reason.empty())
@@ -5196,6 +5203,7 @@ from_json(const nlohmann::json &j, MemberInfo &info)
info.avatar_url = j.value("avatar_url", "");
info.is_direct = j.value("is_direct", false);
info.reason = j.value("reason", "");
+ info.inviter = j.value("inviter", "");
}
void
@@ -5300,13 +5308,6 @@ namespace cache {
void
init(const QString &user_id)
{
- qRegisterMetaType();
- qRegisterMetaType();
- qRegisterMetaType>();
- qRegisterMetaType>();
- qRegisterMetaType>();
- qRegisterMetaType();
-
instance_ = std::make_unique(user_id);
}
diff --git a/src/CacheCryptoStructs.h b/src/CacheCryptoStructs.h
index 96fc35ec..2a5b895f 100644
--- a/src/CacheCryptoStructs.h
+++ b/src/CacheCryptoStructs.h
@@ -5,6 +5,7 @@
#pragma once
#include
+#include
#include