Merge branch 'master' of https://github.com/redsky17/nheko
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
set -ex
|
set -ex
|
||||||
|
|
||||||
if [ $TRAVIS_OS_NAME == osx ]; then
|
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
|
||||||
brew update
|
brew update
|
||||||
brew install qt5 lmdb clang-format ninja libsodium cmark
|
brew install qt5 lmdb clang-format ninja libsodium cmark
|
||||||
brew upgrade boost cmake || true
|
brew upgrade boost cmake icu4c || true
|
||||||
|
|
||||||
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
|
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
|
||||||
sudo python get-pip.py
|
sudo python get-pip.py
|
||||||
|
@ -17,7 +17,7 @@ if [ $TRAVIS_OS_NAME == osx ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
if [ $TRAVIS_OS_NAME == linux ]; then
|
if [ "$TRAVIS_OS_NAME" == "linux" ]; then
|
||||||
|
|
||||||
if [ -z "$QT_VERSION" ]; then
|
if [ -z "$QT_VERSION" ]; then
|
||||||
QT_VERSION="592"
|
QT_VERSION="592"
|
||||||
|
|
|
@ -36,8 +36,10 @@ export LD_LIBRARY_PATH=.deps/usr/lib/:$LD_LIBRARY_PATH
|
||||||
./linuxdeployqt*.AppImage ${DIR}/usr/share/applications/*.desktop -bundle-non-qt-libs
|
./linuxdeployqt*.AppImage ${DIR}/usr/share/applications/*.desktop -bundle-non-qt-libs
|
||||||
./linuxdeployqt*.AppImage ${DIR}/usr/share/applications/*.desktop -appimage
|
./linuxdeployqt*.AppImage ${DIR}/usr/share/applications/*.desktop -appimage
|
||||||
|
|
||||||
chmod +x nheko-x86_64.AppImage
|
chmod +x nheko-*x86_64.AppImage
|
||||||
|
|
||||||
if [ ! -z $TRAVIS_TAG ]; then
|
if [ ! -z $VERSION ]; then
|
||||||
mv nheko-x86_64.AppImage nheko-${TRAVIS_TAG}-x86_64.AppImage
|
# commented out for now, as AppImage file appears to already contain the version.
|
||||||
fi
|
#mv nheko-*x86_64.AppImage nheko-${VERSION}-x86_64.AppImage
|
||||||
|
echo "nheko-${VERSION}-x86_64.AppImage"
|
||||||
|
fi
|
|
@ -8,7 +8,15 @@ TAG=`git tag -l --points-at HEAD`
|
||||||
PATH=/usr/local/opt/qt/bin/:${PATH}
|
PATH=/usr/local/opt/qt/bin/:${PATH}
|
||||||
|
|
||||||
pushd build
|
pushd build
|
||||||
sudo macdeployqt nheko.app -dmg
|
|
||||||
|
# macdeployqt does not copy symlinks over.
|
||||||
|
# this specifically addresses icu4c issues but nothing else.
|
||||||
|
export ICU_LIB="$(brew --prefix icu4c)/lib"
|
||||||
|
mkdir -p nheko.app/Contents/Frameworks
|
||||||
|
find ${ICU_LIB} -type l -name "*.dylib" -exec cp -a -n {} nheko.app/Contents/Frameworks/ \; || true
|
||||||
|
|
||||||
|
sudo macdeployqt nheko.app -dmg -always-overwrite
|
||||||
|
|
||||||
user=$(id -nu)
|
user=$(id -nu)
|
||||||
sudo chown ${user} nheko.dmg
|
sudo chown ${user} nheko.dmg
|
||||||
mv nheko.dmg ..
|
mv nheko.dmg ..
|
||||||
|
@ -16,6 +24,6 @@ popd
|
||||||
|
|
||||||
dmgbuild -s ./.ci/macos/settings.json "Nheko" nheko.dmg
|
dmgbuild -s ./.ci/macos/settings.json "Nheko" nheko.dmg
|
||||||
|
|
||||||
if [ ! -z $TRAVIS_TAG ]; then
|
if [ ! -z $VERSION ]; then
|
||||||
mv nheko.dmg nheko-${TRAVIS_TAG}.dmg
|
mv nheko.dmg nheko-${VERSION}.dmg
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
set -ex
|
set -ex
|
||||||
|
|
||||||
if [ $TRAVIS_OS_NAME == linux ]; then
|
if [ "$TRAVIS_OS_NAME" == "linux" ]; then
|
||||||
export CC=${C_COMPILER}
|
export CC=${C_COMPILER}
|
||||||
export CXX=${CXX_COMPILER}
|
export CXX=${CXX_COMPILER}
|
||||||
|
|
||||||
|
@ -13,11 +13,11 @@ if [ $TRAVIS_OS_NAME == linux ]; then
|
||||||
sudo update-alternatives --set g++ "/usr/bin/${CXX_COMPILER}"
|
sudo update-alternatives --set g++ "/usr/bin/${CXX_COMPILER}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $TRAVIS_OS_NAME == linux ]; then
|
if [ "$TRAVIS_OS_NAME" == "linux" ]; then
|
||||||
source /opt/qt${QT_PKG}/bin/qt${QT_PKG}-env.sh || true;
|
source /opt/qt${QT_PKG}/bin/qt${QT_PKG}-env.sh || true;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $TRAVIS_OS_NAME == osx ]; then
|
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
|
||||||
export CMAKE_PREFIX_PATH=/usr/local/opt/qt5
|
export CMAKE_PREFIX_PATH=/usr/local/opt/qt5
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -33,14 +33,14 @@ cmake -GNinja -H. -Bbuild \
|
||||||
-DCMAKE_INSTALL_PREFIX=.deps/usr
|
-DCMAKE_INSTALL_PREFIX=.deps/usr
|
||||||
cmake --build build
|
cmake --build build
|
||||||
|
|
||||||
if [ $TRAVIS_OS_NAME == osx ]; then
|
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
|
||||||
make lint;
|
make lint;
|
||||||
|
|
||||||
if [ $DEPLOYMENT == 1 ] && [ ! -z $TRAVIS_TAG ]; then
|
if [ $DEPLOYMENT == 1 ] && [ ! -z $VERSION ] ; then
|
||||||
make macos-deploy;
|
make macos-deploy;
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $TRAVIS_OS_NAME == linux ] && [ $DEPLOYMENT == 1 ] && [ ! -z $TRAVIS_TAG ]; then
|
if [ "$TRAVIS_OS_NAME" == "linux" ] && [ $DEPLOYMENT == 1 ] && [ ! -z $VERSION ]; then
|
||||||
make linux-deploy;
|
make linux-deploy;
|
||||||
fi
|
fi
|
||||||
|
|
198
.travis.yml
|
@ -1,118 +1,112 @@
|
||||||
language: cpp
|
language: cpp
|
||||||
sudo: required
|
sudo: required
|
||||||
dist: trusty
|
dist: trusty
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
|
||||||
webhooks:
|
webhooks:
|
||||||
urls:
|
urls:
|
||||||
- https://scalar.vector.im/api/neb/services/hooks/dHJhdmlzLWNpLyU0MHJlZF9za3klM0FvY2Vhbi5qb2Vkb25vZnJ5LmNvbS8lMjFldkFxa1BIWnVQSElHZWVuaGklM0FvY2Vhbi5qb2Vkb25vZnJ5LmNvbQ
|
- https://scalar.vector.im/api/neb/services/hooks/dHJhdmlzLWNpLyU0MHJlZF9za3klM0FvY2Vhbi5qb2Vkb25vZnJ5LmNvbS8lMjFldkFxa1BIWnVQSElHZWVuaGklM0FvY2Vhbi5qb2Vkb25vZnJ5LmNvbQ
|
||||||
on_success: always
|
on_success: always
|
||||||
on_failure: always
|
on_failure: always
|
||||||
on_start: never
|
on_start: never
|
||||||
|
email: false
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: osx
|
- os: osx
|
||||||
osx_image: xcode9
|
compiler: clang
|
||||||
compiler: clang
|
osx_image: xcode9
|
||||||
env:
|
env:
|
||||||
- DEPLOYMENT=1
|
- DEPLOYMENT=1
|
||||||
- USE_BUNDLED_BOOST=0
|
- USE_BUNDLED_BOOST=0
|
||||||
- USE_BUNDLED_CMARK=0
|
- USE_BUNDLED_CMARK=0
|
||||||
- os: linux
|
- os: linux
|
||||||
compiler: gcc
|
compiler: gcc
|
||||||
env:
|
env:
|
||||||
- CXX_COMPILER=g++-5
|
- CXX_COMPILER=g++-5
|
||||||
- C_COMPILER=gcc-5
|
- C_COMPILER=gcc-5
|
||||||
- QT_VERSION="-5.10.1"
|
- QT_VERSION="-5.10.1"
|
||||||
- QT_PKG=510
|
- QT_PKG=510
|
||||||
- DEPLOYMENT=1
|
- DEPLOYMENT=1
|
||||||
- USE_BUNDLED_BOOST=1
|
- USE_BUNDLED_BOOST=1
|
||||||
- USE_BUNDLED_CMARK=1
|
- USE_BUNDLED_CMARK=1
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
sources:
|
sources: ["ubuntu-toolchain-r-test"]
|
||||||
- ubuntu-toolchain-r-test
|
packages: ["g++-5", "ninja-build"]
|
||||||
packages:
|
- os: linux
|
||||||
- g++-5
|
compiler: gcc
|
||||||
- ninja-build
|
env:
|
||||||
- os: linux
|
- CXX_COMPILER=g++-8
|
||||||
compiler: gcc
|
- C_COMPILER=gcc-8
|
||||||
env:
|
- QT_VERSION=571
|
||||||
- CXX_COMPILER=g++-8
|
- QT_PKG=57
|
||||||
- C_COMPILER=gcc-8
|
- USE_BUNDLED_BOOST=1
|
||||||
- QT_VERSION=571
|
- USE_BUNDLED_CMARK=1
|
||||||
- QT_PKG=57
|
addons:
|
||||||
- USE_BUNDLED_BOOST=1
|
apt:
|
||||||
- USE_BUNDLED_CMARK=1
|
sources: ["ubuntu-toolchain-r-test"]
|
||||||
addons:
|
packages: ["g++-8", "ninja-build"]
|
||||||
apt:
|
- os: linux
|
||||||
sources:
|
compiler: clang
|
||||||
- ubuntu-toolchain-r-test
|
env:
|
||||||
packages:
|
- CXX_COMPILER=clang++-5.0
|
||||||
- g++-8
|
- C_COMPILER=clang-5.0
|
||||||
- ninja-build
|
- QT_VERSION=592
|
||||||
- os: linux
|
- QT_PKG=59
|
||||||
compiler: clang
|
- USE_BUNDLED_BOOST=1
|
||||||
env:
|
- USE_BUNDLED_CMARK=1
|
||||||
- CXX_COMPILER=clang++-5.0
|
addons:
|
||||||
- C_COMPILER=clang-5.0
|
apt:
|
||||||
- QT_VERSION=592
|
sources: ["ubuntu-toolchain-r-test", "llvm-toolchain-trusty-5.0"]
|
||||||
- QT_PKG=59
|
packages: ["clang-5.0", "g++-7", "ninja-build"]
|
||||||
- USE_BUNDLED_BOOST=1
|
|
||||||
- USE_BUNDLED_CMARK=1
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
sources:
|
|
||||||
- ubuntu-toolchain-r-test
|
|
||||||
- llvm-toolchain-trusty-5.0
|
|
||||||
packages:
|
|
||||||
- clang-5.0
|
|
||||||
- g++-7
|
|
||||||
- ninja-build
|
|
||||||
before_install:
|
before_install:
|
||||||
- export CXX=${CXX_COMPILER}
|
- export CXX=${CXX_COMPILER}
|
||||||
- export CC=${C_COMPILER}
|
- export CC=${C_COMPILER}
|
||||||
|
# Use TRAVIS_TAG if defined, or the short commit SHA otherwise
|
||||||
|
- export VERSION=${TRAVIS_TAG:-$(git rev-parse --short HEAD)}
|
||||||
install:
|
install:
|
||||||
- "./.ci/install.sh"
|
- ./.ci/install.sh
|
||||||
- export PATH=/usr/local/bin:${PATH}
|
- export PATH=/usr/local/bin:${PATH}
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- "./.ci/script.sh"
|
- ./.ci/script.sh
|
||||||
- sed -i -e "s/VERSION_NAME_VALUE/${TRAVIS_TAG}/g" ./.ci/bintray-release.json || true
|
- sed -i -e "s/VERSION_NAME_VALUE/${VERSION}/g" ./.ci/bintray-release.json || true
|
||||||
- cp ./.ci/bintray-release.json .
|
- cp ./.ci/bintray-release.json .
|
||||||
deploy:
|
deploy:
|
||||||
- provider: bintray
|
- provider: bintray
|
||||||
user: redsky17
|
user: "redsky17"
|
||||||
key:
|
|
||||||
secure: CAVzWZPxYSOTollo9bpD4tvEbfxXjqelc32aApV48GKyJrMQljQ+mvSe25BuUtnDehxnw8affgGX23AYXmvG8P7w4hM2d7//8Lgan1zCmusV8JE432jknev6X641B4cvrywqSe0Dj3l0kS9Xgirq4BGavlI0y2vUjeJfQEv0y8GYoI72LwgyH0i82v/1Qi92Fh8429IJIb0eKmC1wGWXCmo2kd8StZRL5mSlc4TmyWI0SHpA5GrLMiQwLAuD7DjDl5mpaK2yQx+H4vBcI2SUMvmlHGgVjXikJG5gURlHbnIaaBFvO67INc1/65KtMokWuMP12zxqJiaMPtsAskOpQv4FLAYDfnigH3NxufyOIGp2cxS5RhJDQhbNsxHEDnUo1kHcO23ZYNWCuC1yUdn0RXzKhWcUsz8mKF8KJs22Ty4VjfUMZ+vqK/AbHyq4rkl8DizVRZqKF1KjSWrSv/2sT4itnHk9pmcgxAYfGuALcjrJJveI4MTwDhzXB62CKnMOqLq3sAMqvE0+BdA0BykQr7qrKtptuyP2/OFx6RDbfHQl5Klkb6cSOjxm0oUzh/8iaxgsVdCrhfE67eqkhFZ+a8lJkB/rZ4zSK1Q2Cp4nLtnxenUCW+Ptk2l7zZN6kXM1/+tcgqVROChYJ6asMUpsjFOOAVQ8SZ4TcxX1rq+pxlA=
|
|
||||||
skip_cleanup: true
|
|
||||||
overwrite: true
|
|
||||||
file: bintray-release.json
|
|
||||||
on:
|
|
||||||
condition: "$DEPLOYMENT == 1"
|
|
||||||
repo: Nheko-Reborn/nheko
|
|
||||||
tags: true
|
|
||||||
deploy:
|
|
||||||
- skip_cleanup: true
|
|
||||||
overwrite: true
|
|
||||||
provider: releases
|
|
||||||
api_key:
|
|
||||||
secure: JROFCxI1Dj0j8GKftCk1M16PovGmbCQb/i6JKm+YKWIhoYoMJBFl3TzhN0D0KlT8VeClZ0WV4MOom6elAkxlYTGR1kcoJ5ESt/AS0B1ULxq2exbmqzqJgJJBb65JVo4nglLHZPnUHOY5s/QGtg05nPeexcK8b3lFvhMRI+Y5jqX9i4FGsEBk6tG2OLfXB0odU8f6rhEeIGWgJw1LVyiTk3VoQcJtBi7Vsg3p4othMaLDlkVHsepNY+xSO14NbNpUjXSzYWZJEM9HqCOaOlAjZR5Q0Ad365TqN9zj6NOVwxEdN4Zl3/Ux838Or6TobhdhGjqqO2JmWt6C+xV4XJ9wX+8LPb+hYYVBrItp32g3grtW/e4nNsp4j3nm1P87kzKPxC4oAaskyn0dlwC4Vo3LH67beQiceAIuM9ywej4Zwr94+MeKjIVtqI6Qz7Tjlt1pFGI1lmfkKQOXiFlkwPbyCPV3smpJ1WSOC4Npkht6tFPBlLV2DFySYUMRAdH2RwBxWhjzwsSJlx/dEKUUL5yffKtg2tANM6aCCyXMEqEXXVkFe9e9ymPbGmmQuf56xo3rYQj5BcQWA9JHAancqLkxoR0rbRBBmai5qDQP7rBss/HR7Uec5xSnYkS6YYI9zpZ+FTfPa7lnVI3c8hj+ukua1EnsYytB8F8l95jrO8fnTxU=
|
|
||||||
file_glob: true
|
|
||||||
file:
|
|
||||||
- nheko-${TRAVIS_TAG}-x86_64.AppImage
|
|
||||||
on:
|
|
||||||
condition: "$TRAVIS_OS_NAME == linux && $DEPLOYMENT == 1"
|
|
||||||
repo: Nheko-Reborn/nheko
|
|
||||||
tags: true
|
|
||||||
- skip_cleanup: true
|
|
||||||
overwrite: true
|
|
||||||
provider: releases
|
|
||||||
api_key:
|
|
||||||
secure: JROFCxI1Dj0j8GKftCk1M16PovGmbCQb/i6JKm+YKWIhoYoMJBFl3TzhN0D0KlT8VeClZ0WV4MOom6elAkxlYTGR1kcoJ5ESt/AS0B1ULxq2exbmqzqJgJJBb65JVo4nglLHZPnUHOY5s/QGtg05nPeexcK8b3lFvhMRI+Y5jqX9i4FGsEBk6tG2OLfXB0odU8f6rhEeIGWgJw1LVyiTk3VoQcJtBi7Vsg3p4othMaLDlkVHsepNY+xSO14NbNpUjXSzYWZJEM9HqCOaOlAjZR5Q0Ad365TqN9zj6NOVwxEdN4Zl3/Ux838Or6TobhdhGjqqO2JmWt6C+xV4XJ9wX+8LPb+hYYVBrItp32g3grtW/e4nNsp4j3nm1P87kzKPxC4oAaskyn0dlwC4Vo3LH67beQiceAIuM9ywej4Zwr94+MeKjIVtqI6Qz7Tjlt1pFGI1lmfkKQOXiFlkwPbyCPV3smpJ1WSOC4Npkht6tFPBlLV2DFySYUMRAdH2RwBxWhjzwsSJlx/dEKUUL5yffKtg2tANM6aCCyXMEqEXXVkFe9e9ymPbGmmQuf56xo3rYQj5BcQWA9JHAancqLkxoR0rbRBBmai5qDQP7rBss/HR7Uec5xSnYkS6YYI9zpZ+FTfPa7lnVI3c8hj+ukua1EnsYytB8F8l95jrO8fnTxU=
|
|
||||||
file: nheko-${TRAVIS_TAG}.dmg
|
|
||||||
on:
|
|
||||||
condition: "$TRAVIS_OS_NAME == osx && $DEPLOYMENT == 1"
|
|
||||||
repo: Nheko-Reborn/nheko
|
|
||||||
tags: true
|
|
||||||
key:
|
key:
|
||||||
secure: q4V4k6mAEDBgA/13NiCw+5/Jh7/xmtRBybFSr/0I6JTatkaLs2pj4zIRyHIrBVZtOd1oFVmq6aDHXXR+fbSo1Euj3s5Eo+7TTAqKAlyRMcme/+0S0bfZfisA+6LskZcmacq1FzEkAXd5OjMXB6rIakDC1sgkgo5bpM9w5r/9ZFXXgCfDzW07LJOypDyph0Gg4rlU22o5fAcKmuglTgbJWceznv46HcHvW1s2JzkJQugpAh8LkpiEkuNnH1wZ0WDI1wQQFI+ti5GSBkHicS2kgkOL3IlCvfzS0ym85XF1FTncqDEClxudwWOhVm3qpSOm28I+lB4i0ha1LNzsl4S8ClVTxJRJMJBHmLmkh3lOasAn6v8Vc2WASygfnTC2VGMaRWYMfphLm7e1CcT8OPfoNcEJLvR6YTxgm7AadomOV4f8q9FUwvrkyJkbR+sV+DkJ5yQ/uF1pDOMnUUzjDYpCfYXEECqh8gH8iUXhWabrJyaFlzZaOsai/ujyepLOkUtJaGcOrnCHlOQlfXgBhmOCUFau8ByJhSrHGGlBPb9JhC/jzWq++dmN/5zn1coc4kNqKB55h1AFVtTTW7t14RzNKER2/opl7LFoywvyMyERusmxHfGzNihFHO4GoBY+WtEpphCAdqCjLaJM95w9spQ0sgR0/qy4883MhTWBctT9K6s=
|
secure: "2C+ESOClZdNCDzfUfE8Rcdn9+BM3/5x1lebGS0qRxICMAuylx50C8RAxlqKIFNI1J+IMnj5xhK+1oWsLg6wUdo8B3JocvM6P1NbC/WwfY5UJVwtSmzcqvEC+IjM90Bng8OoM5f6ED3IAUyYPBgo8J14+2l5Oruvr99t0mrpniLXDf66TJrI9u/+05JYa2pm0EWlQkDVaziI+96zAt1pMIX9Z/ToTmUbq2OAlHaN8H4hR5aBRmYuF11FQVyARK64eqRsBV4RXKJ9DjW7BR+pQV008hQUoXrCxK8xIf/Qph9h1fz63961NJxfru62LeuZ4Z8MUcV9L+p0HYZzkhNdn5Z3QFf3SXbrVCYs6LK6+uKHpmYS7vB5XwjDx9SWc2szsd5OVKJg3YEdBcC4KUSL3VEbBHDSQ3GFsVKoBsZvopwHkyCmPotY47HtEiPBLdEcsOvcVyeU5Gfq9EOp9dB+nJbNrujBgav1l13CorkBG2rgD+NRivV1otclXnPsIj85ySrc6xk0VDN3zogLAdLw0f+fNOaK17N8GmhPbCu/iX2Vjem/fdjh4NPC4pSNDei2MoWUfqGBmoTxuQEsZeywigwV2c51W9RZuwo8X810N3ehVcBmmNyLjbKmYT+8Kp+dJbaf/ErFnvdZl06mX2bgyxFakv5iQY3dwX4WYHvl77c4="
|
||||||
|
skip_cleanup: true
|
||||||
|
overwrite: true
|
||||||
|
file: bintray-release.json
|
||||||
|
on:
|
||||||
|
condition: "$DEPLOYMENT == 1"
|
||||||
|
repo: Nheko-Reborn/nheko
|
||||||
|
tags: false
|
||||||
|
all_branches: true
|
||||||
|
deploy:
|
||||||
|
- skip_cleanup: true
|
||||||
|
overwrite: true
|
||||||
|
provider: releases
|
||||||
|
api_key:
|
||||||
|
secure: "JROFCxI1Dj0j8GKftCk1M16PovGmbCQb/i6JKm+YKWIhoYoMJBFl3TzhN0D0KlT8VeClZ0WV4MOom6elAkxlYTGR1kcoJ5ESt/AS0B1ULxq2exbmqzqJgJJBb65JVo4nglLHZPnUHOY5s/QGtg05nPeexcK8b3lFvhMRI+Y5jqX9i4FGsEBk6tG2OLfXB0odU8f6rhEeIGWgJw1LVyiTk3VoQcJtBi7Vsg3p4othMaLDlkVHsepNY+xSO14NbNpUjXSzYWZJEM9HqCOaOlAjZR5Q0Ad365TqN9zj6NOVwxEdN4Zl3/Ux838Or6TobhdhGjqqO2JmWt6C+xV4XJ9wX+8LPb+hYYVBrItp32g3grtW/e4nNsp4j3nm1P87kzKPxC4oAaskyn0dlwC4Vo3LH67beQiceAIuM9ywej4Zwr94+MeKjIVtqI6Qz7Tjlt1pFGI1lmfkKQOXiFlkwPbyCPV3smpJ1WSOC4Npkht6tFPBlLV2DFySYUMRAdH2RwBxWhjzwsSJlx/dEKUUL5yffKtg2tANM6aCCyXMEqEXXVkFe9e9ymPbGmmQuf56xo3rYQj5BcQWA9JHAancqLkxoR0rbRBBmai5qDQP7rBss/HR7Uec5xSnYkS6YYI9zpZ+FTfPa7lnVI3c8hj+ukua1EnsYytB8F8l95jrO8fnTxU="
|
||||||
|
file_glob: true
|
||||||
|
file:
|
||||||
|
- nheko-${VERSION}-x86_64.AppImage
|
||||||
|
on:
|
||||||
|
condition: "$TRAVIS_OS_NAME == linux && $DEPLOYMENT == 1"
|
||||||
|
repo: Nheko-Reborn/nheko
|
||||||
|
tags: true
|
||||||
|
- skip_cleanup: true
|
||||||
|
overwrite: true
|
||||||
|
provider: releases
|
||||||
|
api_key:
|
||||||
|
secure: "JROFCxI1Dj0j8GKftCk1M16PovGmbCQb/i6JKm+YKWIhoYoMJBFl3TzhN0D0KlT8VeClZ0WV4MOom6elAkxlYTGR1kcoJ5ESt/AS0B1ULxq2exbmqzqJgJJBb65JVo4nglLHZPnUHOY5s/QGtg05nPeexcK8b3lFvhMRI+Y5jqX9i4FGsEBk6tG2OLfXB0odU8f6rhEeIGWgJw1LVyiTk3VoQcJtBi7Vsg3p4othMaLDlkVHsepNY+xSO14NbNpUjXSzYWZJEM9HqCOaOlAjZR5Q0Ad365TqN9zj6NOVwxEdN4Zl3/Ux838Or6TobhdhGjqqO2JmWt6C+xV4XJ9wX+8LPb+hYYVBrItp32g3grtW/e4nNsp4j3nm1P87kzKPxC4oAaskyn0dlwC4Vo3LH67beQiceAIuM9ywej4Zwr94+MeKjIVtqI6Qz7Tjlt1pFGI1lmfkKQOXiFlkwPbyCPV3smpJ1WSOC4Npkht6tFPBlLV2DFySYUMRAdH2RwBxWhjzwsSJlx/dEKUUL5yffKtg2tANM6aCCyXMEqEXXVkFe9e9ymPbGmmQuf56xo3rYQj5BcQWA9JHAancqLkxoR0rbRBBmai5qDQP7rBss/HR7Uec5xSnYkS6YYI9zpZ+FTfPa7lnVI3c8hj+ukua1EnsYytB8F8l95jrO8fnTxU="
|
||||||
|
file: nheko-${VERSION}.dmg
|
||||||
|
on:
|
||||||
|
condition: "$TRAVIS_OS_NAME == osx && $DEPLOYMENT == 1"
|
||||||
|
repo: Nheko-Reborn/nheko
|
||||||
|
tags: true
|
|
@ -14,8 +14,6 @@
|
||||||
|
|
||||||
### Other changes
|
### Other changes
|
||||||
- Removed room re-ordering option.
|
- Removed room re-ordering option.
|
||||||
- Removed built-in emoji picker.
|
|
||||||
- Removed bundled fonts.
|
|
||||||
|
|
||||||
## [0.6.1] - 2018-09-26
|
## [0.6.1] - 2018-09-26
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
cmake_minimum_required(VERSION 3.1)
|
cmake_minimum_required(VERSION 3.11)
|
||||||
|
|
||||||
option(APPVEYOR_BUILD "Build on appveyor" OFF)
|
option(APPVEYOR_BUILD "Build on appveyor" OFF)
|
||||||
option(ASAN "Compile with address sanitizers" OFF)
|
option(ASAN "Compile with address sanitizers" OFF)
|
||||||
|
@ -183,6 +183,10 @@ set(SRC_FILES
|
||||||
src/dialogs/RoomSettings.cpp
|
src/dialogs/RoomSettings.cpp
|
||||||
|
|
||||||
# Emoji
|
# Emoji
|
||||||
|
src/emoji/Category.cpp
|
||||||
|
src/emoji/ItemDelegate.cpp
|
||||||
|
src/emoji/Panel.cpp
|
||||||
|
src/emoji/PickButton.cpp
|
||||||
src/emoji/Provider.cpp
|
src/emoji/Provider.cpp
|
||||||
|
|
||||||
# Timeline
|
# Timeline
|
||||||
|
@ -289,6 +293,9 @@ include_directories(SYSTEM ${TWEENY_INCLUDE_DIR})
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/src)
|
include_directories(${CMAKE_SOURCE_DIR}/src)
|
||||||
include_directories(${Boost_INCLUDE_DIRS})
|
include_directories(${Boost_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
# local inclue directory
|
||||||
|
include_directories(includes)
|
||||||
|
|
||||||
qt5_wrap_cpp(MOC_HEADERS
|
qt5_wrap_cpp(MOC_HEADERS
|
||||||
# Dialogs
|
# Dialogs
|
||||||
src/dialogs/CreateRoom.h
|
src/dialogs/CreateRoom.h
|
||||||
|
@ -305,6 +312,12 @@ qt5_wrap_cpp(MOC_HEADERS
|
||||||
src/dialogs/ReCaptcha.h
|
src/dialogs/ReCaptcha.h
|
||||||
src/dialogs/RoomSettings.h
|
src/dialogs/RoomSettings.h
|
||||||
|
|
||||||
|
# Emoji
|
||||||
|
src/emoji/Category.h
|
||||||
|
src/emoji/ItemDelegate.h
|
||||||
|
src/emoji/Panel.h
|
||||||
|
src/emoji/PickButton.h
|
||||||
|
|
||||||
# Timeline
|
# Timeline
|
||||||
src/timeline/TimelineItem.h
|
src/timeline/TimelineItem.h
|
||||||
src/timeline/TimelineView.h
|
src/timeline/TimelineView.h
|
||||||
|
|
|
@ -264,5 +264,11 @@ Here is a screen shot to get a feel for the UI, but things will probably change.
|
||||||
|
|
||||||
![nheko](https://dl.dropboxusercontent.com/s/3zjcezdtk8kqe4i/nheko-v0.4.0.png)
|
![nheko](https://dl.dropboxusercontent.com/s/3zjcezdtk8kqe4i/nheko-v0.4.0.png)
|
||||||
|
|
||||||
|
### Third party
|
||||||
|
|
||||||
|
- [Emoji One](http://emojione.com)
|
||||||
|
- [Font Awesome](http://fontawesome.io/)
|
||||||
|
- [Open Sans](https://fonts.google.com/specimen/Open+Sans)
|
||||||
|
|
||||||
[Matrix]:https://matrix.org
|
[Matrix]:https://matrix.org
|
||||||
[Riot]:https://riot.im
|
[Riot]:https://riot.im
|
||||||
|
|
7
deps/CMakeLists.txt
vendored
|
@ -1,4 +1,4 @@
|
||||||
cmake_minimum_required(VERSION 3.1)
|
cmake_minimum_required(VERSION 3.11)
|
||||||
project(NHEKO_DEPS)
|
project(NHEKO_DEPS)
|
||||||
|
|
||||||
# Point CMake at any custom modules we may ship
|
# Point CMake at any custom modules we may ship
|
||||||
|
@ -30,6 +30,11 @@ option(USE_BUNDLED_LMDBXX "Use the bundled version of lmdbxx." ${USE_BUNDLED})
|
||||||
option(USE_BUNDLED_MATRIX_CLIENT "Use the bundled version of mtxclient."
|
option(USE_BUNDLED_MATRIX_CLIENT "Use the bundled version of mtxclient."
|
||||||
${USE_BUNDLED})
|
${USE_BUNDLED})
|
||||||
|
|
||||||
|
if(USE_BUNDLED_BOOST)
|
||||||
|
# bundled boost is 1.68, which requires CMake 3.12 or greater.
|
||||||
|
cmake_minimum_required(VERSION 3.12)
|
||||||
|
endif()
|
||||||
|
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
|
|
||||||
set(BOOST_URL
|
set(BOOST_URL
|
||||||
|
|
17
includes/jdenticoninterface.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef JDENTICONINTERFACE_H
|
||||||
|
#define JDENTICONINTERFACE_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class JdenticonInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~JdenticonInterface() {}
|
||||||
|
virtual QString generate(const QString &message, uint16_t size) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define JdenticonInterface_iid "redsky17.Qt.JdenticonInterface"
|
||||||
|
|
||||||
|
Q_DECLARE_INTERFACE(JdenticonInterface, JdenticonInterface_iid)
|
||||||
|
|
||||||
|
#endif // JDENTICONINTERFACE_H
|
BIN
resources/fonts/EmojiOne/emojione-android.ttf
Normal file
202
resources/fonts/OpenSans/LICENSE.txt
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
BIN
resources/fonts/OpenSans/OpenSans-Bold.ttf
Normal file
BIN
resources/fonts/OpenSans/OpenSans-BoldItalic.ttf
Normal file
BIN
resources/fonts/OpenSans/OpenSans-ExtraBold.ttf
Normal file
BIN
resources/fonts/OpenSans/OpenSans-ExtraBoldItalic.ttf
Normal file
BIN
resources/fonts/OpenSans/OpenSans-Italic.ttf
Normal file
BIN
resources/fonts/OpenSans/OpenSans-Light.ttf
Normal file
BIN
resources/fonts/OpenSans/OpenSans-LightItalic.ttf
Normal file
BIN
resources/fonts/OpenSans/OpenSans-Regular.ttf
Normal file
BIN
resources/fonts/OpenSans/OpenSans-Semibold.ttf
Normal file
BIN
resources/fonts/OpenSans/OpenSans-SemiboldItalic.ttf
Normal file
BIN
resources/icons/emoji-categories/activity.png
Normal file
After Width: | Height: | Size: 603 B |
BIN
resources/icons/emoji-categories/activity@2x.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
resources/icons/emoji-categories/flags.png
Normal file
After Width: | Height: | Size: 416 B |
BIN
resources/icons/emoji-categories/flags@2x.png
Normal file
After Width: | Height: | Size: 824 B |
BIN
resources/icons/emoji-categories/foods.png
Normal file
After Width: | Height: | Size: 537 B |
BIN
resources/icons/emoji-categories/foods@2x.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/icons/emoji-categories/nature.png
Normal file
After Width: | Height: | Size: 667 B |
BIN
resources/icons/emoji-categories/nature@2x.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
resources/icons/emoji-categories/objects.png
Normal file
After Width: | Height: | Size: 606 B |
BIN
resources/icons/emoji-categories/objects@2x.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
resources/icons/emoji-categories/people.png
Normal file
After Width: | Height: | Size: 581 B |
BIN
resources/icons/emoji-categories/people@2x.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
resources/icons/emoji-categories/symbols.png
Normal file
After Width: | Height: | Size: 504 B |
BIN
resources/icons/emoji-categories/symbols@2x.png
Normal file
After Width: | Height: | Size: 1,001 B |
BIN
resources/icons/emoji-categories/travel.png
Normal file
After Width: | Height: | Size: 439 B |
BIN
resources/icons/emoji-categories/travel@2x.png
Normal file
After Width: | Height: | Size: 840 B |
|
@ -63,6 +63,22 @@
|
||||||
<file>icons/ui/edit.png</file>
|
<file>icons/ui/edit.png</file>
|
||||||
<file>icons/ui/edit@2x.png</file>
|
<file>icons/ui/edit@2x.png</file>
|
||||||
|
|
||||||
|
<file>icons/emoji-categories/people.png</file>
|
||||||
|
<file>icons/emoji-categories/people@2x.png</file>
|
||||||
|
<file>icons/emoji-categories/nature.png</file>
|
||||||
|
<file>icons/emoji-categories/nature@2x.png</file>
|
||||||
|
<file>icons/emoji-categories/foods.png</file>
|
||||||
|
<file>icons/emoji-categories/foods@2x.png</file>
|
||||||
|
<file>icons/emoji-categories/activity.png</file>
|
||||||
|
<file>icons/emoji-categories/activity@2x.png</file>
|
||||||
|
<file>icons/emoji-categories/travel.png</file>
|
||||||
|
<file>icons/emoji-categories/travel@2x.png</file>
|
||||||
|
<file>icons/emoji-categories/objects.png</file>
|
||||||
|
<file>icons/emoji-categories/objects@2x.png</file>
|
||||||
|
<file>icons/emoji-categories/symbols.png</file>
|
||||||
|
<file>icons/emoji-categories/symbols@2x.png</file>
|
||||||
|
<file>icons/emoji-categories/flags.png</file>
|
||||||
|
<file>icons/emoji-categories/flags@2x.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/logos">
|
<qresource prefix="/logos">
|
||||||
<file>nheko.png</file>
|
<file>nheko.png</file>
|
||||||
|
@ -83,6 +99,13 @@
|
||||||
<file>nheko-32.png</file>
|
<file>nheko-32.png</file>
|
||||||
<file>nheko-16.png</file>
|
<file>nheko-16.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
|
<qresource prefix="/fonts">
|
||||||
|
<file>fonts/OpenSans/OpenSans-Regular.ttf</file>
|
||||||
|
<file>fonts/OpenSans/OpenSans-Italic.ttf</file>
|
||||||
|
<file>fonts/OpenSans/OpenSans-Bold.ttf</file>
|
||||||
|
<file>fonts/OpenSans/OpenSans-Semibold.ttf</file>
|
||||||
|
<file>fonts/EmojiOne/emojione-android.ttf</file>
|
||||||
|
</qresource>
|
||||||
<qresource prefix="/styles">
|
<qresource prefix="/styles">
|
||||||
<file>styles/system.qss</file>
|
<file>styles/system.qss</file>
|
||||||
<file>styles/nheko.qss</file>
|
<file>styles/nheko.qss</file>
|
||||||
|
|
|
@ -3,6 +3,10 @@ QLabel {
|
||||||
color: #caccd1;
|
color: #caccd1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimelineItem {
|
||||||
|
qproperty-backgroundColor: #202228;
|
||||||
|
}
|
||||||
|
|
||||||
#chatPage,
|
#chatPage,
|
||||||
#chatPage > * {
|
#chatPage > * {
|
||||||
background-color: #202228;
|
background-color: #202228;
|
||||||
|
@ -86,6 +90,7 @@ RaisedButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
RoomInfoListItem {
|
RoomInfoListItem {
|
||||||
|
qproperty-mentionedColor: #a82353;
|
||||||
qproperty-highlightedBackgroundColor: #4d84c7;
|
qproperty-highlightedBackgroundColor: #4d84c7;
|
||||||
qproperty-hoverBackgroundColor: rgba(230, 230, 230, 30);
|
qproperty-hoverBackgroundColor: rgba(230, 230, 230, 30);
|
||||||
qproperty-backgroundColor: #2d3139;
|
qproperty-backgroundColor: #2d3139;
|
||||||
|
@ -188,6 +193,18 @@ RegisterPage {
|
||||||
color: #caccd1;
|
color: #caccd1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emoji--Panel,
|
||||||
|
emoji--Panel > * {
|
||||||
|
background-color: #202228;
|
||||||
|
color: #caccd1;
|
||||||
|
}
|
||||||
|
|
||||||
|
emoji--Category,
|
||||||
|
emoji--Category > * {
|
||||||
|
background-color: #2d3139;
|
||||||
|
color: #caccd1;
|
||||||
|
}
|
||||||
|
|
||||||
FloatingButton {
|
FloatingButton {
|
||||||
qproperty-backgroundColor: #2d3139;
|
qproperty-backgroundColor: #2d3139;
|
||||||
qproperty-foregroundColor: white;
|
qproperty-foregroundColor: white;
|
||||||
|
|
|
@ -3,6 +3,10 @@ QLabel {
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimelineItem {
|
||||||
|
qproperty-backgroundColor: white;
|
||||||
|
}
|
||||||
|
|
||||||
#chatPage,
|
#chatPage,
|
||||||
#chatPage > * {
|
#chatPage > * {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
@ -83,6 +87,7 @@ RaisedButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
RoomInfoListItem {
|
RoomInfoListItem {
|
||||||
|
qproperty-mentionedColor: #a82353;
|
||||||
qproperty-highlightedBackgroundColor: #38A3D8;
|
qproperty-highlightedBackgroundColor: #38A3D8;
|
||||||
qproperty-hoverBackgroundColor: rgba(200, 200, 200, 70);
|
qproperty-hoverBackgroundColor: rgba(200, 200, 200, 70);
|
||||||
qproperty-hoverTitleColor: #f2f5f8;
|
qproperty-hoverTitleColor: #f2f5f8;
|
||||||
|
@ -185,6 +190,18 @@ RegisterPage {
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emoji--Panel,
|
||||||
|
emoji--Panel > * {
|
||||||
|
background-color: #eee;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
emoji--Category,
|
||||||
|
emoji--Category > * {
|
||||||
|
background-color: white;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
FloatingButton {
|
FloatingButton {
|
||||||
qproperty-backgroundColor: #efefef;
|
qproperty-backgroundColor: #efefef;
|
||||||
qproperty-foregroundColor: black;
|
qproperty-foregroundColor: black;
|
||||||
|
|
|
@ -3,6 +3,10 @@ TypingDisplay {
|
||||||
qproperty-backgroundColor: palette(window);
|
qproperty-backgroundColor: palette(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimelineItem {
|
||||||
|
qproperty-backgroundColor: palette(window);
|
||||||
|
}
|
||||||
|
|
||||||
TimelineView,
|
TimelineView,
|
||||||
TimelineView > * {
|
TimelineView > * {
|
||||||
border: none;
|
border: none;
|
||||||
|
@ -82,6 +86,7 @@ QListWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
RoomInfoListItem {
|
RoomInfoListItem {
|
||||||
|
qproperty-mentionedColor: palette(alternate-base);
|
||||||
qproperty-highlightedBackgroundColor: palette(highlight);
|
qproperty-highlightedBackgroundColor: palette(highlight);
|
||||||
qproperty-hoverBackgroundColor: palette(base);
|
qproperty-hoverBackgroundColor: palette(base);
|
||||||
qproperty-backgroundColor: palette(window);
|
qproperty-backgroundColor: palette(window);
|
||||||
|
|
|
@ -2059,6 +2059,7 @@ Cache::roomMembers(const std::string &room_id)
|
||||||
|
|
||||||
QHash<QString, QString> Cache::DisplayNames;
|
QHash<QString, QString> Cache::DisplayNames;
|
||||||
QHash<QString, QString> Cache::AvatarUrls;
|
QHash<QString, QString> Cache::AvatarUrls;
|
||||||
|
QHash<QString, QString> Cache::UserColors;
|
||||||
|
|
||||||
QString
|
QString
|
||||||
Cache::displayName(const QString &room_id, const QString &user_id)
|
Cache::displayName(const QString &room_id, const QString &user_id)
|
||||||
|
@ -2090,6 +2091,16 @@ Cache::avatarUrl(const QString &room_id, const QString &user_id)
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
Cache::userColor(const QString &user_id)
|
||||||
|
{
|
||||||
|
if (UserColors.contains(user_id)) {
|
||||||
|
return UserColors[user_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Cache::insertDisplayName(const QString &room_id,
|
Cache::insertDisplayName(const QString &room_id,
|
||||||
const QString &user_id,
|
const QString &user_id,
|
||||||
|
@ -2119,3 +2130,21 @@ Cache::removeAvatarUrl(const QString &room_id, const QString &user_id)
|
||||||
auto fmt = QString("%1 %2").arg(room_id).arg(user_id);
|
auto fmt = QString("%1 %2").arg(room_id).arg(user_id);
|
||||||
AvatarUrls.remove(fmt);
|
AvatarUrls.remove(fmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Cache::insertUserColor(const QString &user_id, const QString &color_name)
|
||||||
|
{
|
||||||
|
UserColors.insert(user_id, color_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Cache::removeUserColor(const QString &user_id)
|
||||||
|
{
|
||||||
|
UserColors.remove(user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Cache::clearUserColors()
|
||||||
|
{
|
||||||
|
UserColors.clear();
|
||||||
|
}
|
|
@ -282,13 +282,16 @@ public:
|
||||||
|
|
||||||
static QHash<QString, QString> DisplayNames;
|
static QHash<QString, QString> DisplayNames;
|
||||||
static QHash<QString, QString> AvatarUrls;
|
static QHash<QString, QString> AvatarUrls;
|
||||||
|
static QHash<QString, QString> UserColors;
|
||||||
|
|
||||||
static std::string displayName(const std::string &room_id, const std::string &user_id);
|
static std::string displayName(const std::string &room_id, const std::string &user_id);
|
||||||
static QString displayName(const QString &room_id, const QString &user_id);
|
static QString displayName(const QString &room_id, const QString &user_id);
|
||||||
static QString avatarUrl(const QString &room_id, const QString &user_id);
|
static QString avatarUrl(const QString &room_id, const QString &user_id);
|
||||||
|
static QString userColor(const QString &user_id);
|
||||||
|
|
||||||
static void removeDisplayName(const QString &room_id, const QString &user_id);
|
static void removeDisplayName(const QString &room_id, const QString &user_id);
|
||||||
static void removeAvatarUrl(const QString &room_id, const QString &user_id);
|
static void removeAvatarUrl(const QString &room_id, const QString &user_id);
|
||||||
|
static void removeUserColor(const QString &user_id);
|
||||||
|
|
||||||
static void insertDisplayName(const QString &room_id,
|
static void insertDisplayName(const QString &room_id,
|
||||||
const QString &user_id,
|
const QString &user_id,
|
||||||
|
@ -296,6 +299,9 @@ public:
|
||||||
static void insertAvatarUrl(const QString &room_id,
|
static void insertAvatarUrl(const QString &room_id,
|
||||||
const QString &user_id,
|
const QString &user_id,
|
||||||
const QString &avatar_url);
|
const QString &avatar_url);
|
||||||
|
static void insertUserColor(const QString &user_id, const QString &color_name);
|
||||||
|
|
||||||
|
static void clearUserColors();
|
||||||
|
|
||||||
//! Load saved data for the display names & avatars.
|
//! Load saved data for the display names & avatars.
|
||||||
void populateMembers();
|
void populateMembers();
|
||||||
|
|
|
@ -546,7 +546,9 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
||||||
|
|
||||||
updateTypingUsers(room_id, room.second.ephemeral.typing);
|
updateTypingUsers(room_id, room.second.ephemeral.typing);
|
||||||
updateRoomNotificationCount(
|
updateRoomNotificationCount(
|
||||||
room_id, room.second.unread_notifications.notification_count);
|
room_id,
|
||||||
|
room.second.unread_notifications.notification_count,
|
||||||
|
room.second.unread_notifications.highlight_count);
|
||||||
|
|
||||||
if (room.second.unread_notifications.notification_count > 0)
|
if (room.second.unread_notifications.notification_count > 0)
|
||||||
hasNotifications = true;
|
hasNotifications = true;
|
||||||
|
@ -908,9 +910,11 @@ ChatPage::setGroupViewState(bool isEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ChatPage::updateRoomNotificationCount(const QString &room_id, uint16_t notification_count)
|
ChatPage::updateRoomNotificationCount(const QString &room_id,
|
||||||
|
uint16_t notification_count,
|
||||||
|
uint16_t highlight_count)
|
||||||
{
|
{
|
||||||
room_list_->updateUnreadMessageCount(room_id, notification_count);
|
room_list_->updateUnreadMessageCount(room_id, notification_count, highlight_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1098,7 +1102,8 @@ ChatPage::trySync()
|
||||||
void
|
void
|
||||||
ChatPage::joinRoom(const QString &room)
|
ChatPage::joinRoom(const QString &room)
|
||||||
{
|
{
|
||||||
const auto room_id = room.toStdString();
|
// Percent escape the room ID
|
||||||
|
const auto room_id = QUrl::toPercentEncoding(room).toStdString();
|
||||||
|
|
||||||
http::client()->join_room(
|
http::client()->join_room(
|
||||||
room_id, [this, room_id](const nlohmann::json &, mtx::http::RequestErr err) {
|
room_id, [this, room_id](const nlohmann::json &, mtx::http::RequestErr err) {
|
||||||
|
|
|
@ -148,6 +148,7 @@ signals:
|
||||||
const QImage &icon);
|
const QImage &icon);
|
||||||
|
|
||||||
void updateGroupsInfo(const mtx::responses::JoinedGroups &groups);
|
void updateGroupsInfo(const mtx::responses::JoinedGroups &groups);
|
||||||
|
void themeChanged();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void showUnreadMessageNotification(int count);
|
void showUnreadMessageNotification(int count);
|
||||||
|
@ -196,7 +197,9 @@ private:
|
||||||
Memberships getMemberships(const std::vector<Collection> &events) const;
|
Memberships getMemberships(const std::vector<Collection> &events) const;
|
||||||
|
|
||||||
//! Update the room with the new notification count.
|
//! Update the room with the new notification count.
|
||||||
void updateRoomNotificationCount(const QString &room_id, uint16_t notification_count);
|
void updateRoomNotificationCount(const QString &room_id,
|
||||||
|
uint16_t notification_count,
|
||||||
|
uint16_t highlight_count);
|
||||||
//! Send desktop notification for the received messages.
|
//! Send desktop notification for the received messages.
|
||||||
void sendDesktopNotifications(const mtx::responses::Notifications &);
|
void sendDesktopNotifications(const mtx::responses::Notifications &);
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QLayout>
|
#include <QLayout>
|
||||||
|
#include <QPluginLoader>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QShortcut>
|
#include <QShortcut>
|
||||||
|
|
||||||
|
@ -112,7 +113,11 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
|
|
||||||
connect(
|
connect(
|
||||||
userSettingsPage_, SIGNAL(trayOptionChanged(bool)), trayIcon_, SLOT(setVisible(bool)));
|
userSettingsPage_, SIGNAL(trayOptionChanged(bool)), trayIcon_, SLOT(setVisible(bool)));
|
||||||
|
connect(userSettingsPage_, &UserSettingsPage::themeChanged, this, []() {
|
||||||
|
Cache::clearUserColors();
|
||||||
|
});
|
||||||
|
connect(
|
||||||
|
userSettingsPage_, &UserSettingsPage::themeChanged, chat_page_, &ChatPage::themeChanged);
|
||||||
connect(trayIcon_,
|
connect(trayIcon_,
|
||||||
SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
||||||
this,
|
this,
|
||||||
|
@ -162,6 +167,10 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
|
|
||||||
showChatPage();
|
showChatPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (loadJdenticonPlugin()) {
|
||||||
|
nhlog::ui()->info("loaded jdenticon.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -475,3 +484,27 @@ MainWindow::showDialog(QWidget *dialog)
|
||||||
dialog->raise();
|
dialog->raise();
|
||||||
dialog->show();
|
dialog->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
MainWindow::loadJdenticonPlugin()
|
||||||
|
{
|
||||||
|
QDir pluginsDir(qApp->applicationDirPath());
|
||||||
|
|
||||||
|
bool plugins = pluginsDir.cd("plugins");
|
||||||
|
if (plugins) {
|
||||||
|
foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
|
||||||
|
QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
|
||||||
|
QObject *plugin = pluginLoader.instance();
|
||||||
|
if (plugin) {
|
||||||
|
jdenticonInteface_ = qobject_cast<JdenticonInterface *>(plugin);
|
||||||
|
if (jdenticonInteface_) {
|
||||||
|
nhlog::ui()->info("Found jdenticon plugin.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nhlog::ui()->info("jdenticon plugin not found.");
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -31,6 +31,8 @@
|
||||||
#include "dialogs/UserProfile.h"
|
#include "dialogs/UserProfile.h"
|
||||||
#include "ui/OverlayModal.h"
|
#include "ui/OverlayModal.h"
|
||||||
|
|
||||||
|
#include "jdenticoninterface.h"
|
||||||
|
|
||||||
class ChatPage;
|
class ChatPage;
|
||||||
class LoadingIndicator;
|
class LoadingIndicator;
|
||||||
class OverlayModal;
|
class OverlayModal;
|
||||||
|
@ -129,6 +131,8 @@ private slots:
|
||||||
void removeOverlayProgressBar();
|
void removeOverlayProgressBar();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool loadJdenticonPlugin();
|
||||||
|
|
||||||
void showDialog(QWidget *dialog);
|
void showDialog(QWidget *dialog);
|
||||||
bool hasActiveUser();
|
bool hasActiveUser();
|
||||||
void restoreWindowSize();
|
void restoreWindowSize();
|
||||||
|
@ -158,4 +162,6 @@ private:
|
||||||
//! Overlay modal used to project other widgets.
|
//! Overlay modal used to project other widgets.
|
||||||
OverlayModal *modal_ = nullptr;
|
OverlayModal *modal_ = nullptr;
|
||||||
LoadingIndicator *spinner_ = nullptr;
|
LoadingIndicator *spinner_ = nullptr;
|
||||||
|
|
||||||
|
JdenticonInterface *jdenticonInteface_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
|
@ -101,6 +101,7 @@ RoomInfoListItem::RoomInfoListItem(QString room_id, RoomInfo info, QWidget *pare
|
||||||
, roomName_{QString::fromStdString(std::move(info.name))}
|
, roomName_{QString::fromStdString(std::move(info.name))}
|
||||||
, isPressed_(false)
|
, isPressed_(false)
|
||||||
, unreadMsgCount_(0)
|
, unreadMsgCount_(0)
|
||||||
|
, unreadHighlightedMsgCount_(0)
|
||||||
{
|
{
|
||||||
init(parent);
|
init(parent);
|
||||||
|
|
||||||
|
@ -301,7 +302,11 @@ RoomInfoListItem::paintEvent(QPaintEvent *event)
|
||||||
if (unreadMsgCount_ > 0) {
|
if (unreadMsgCount_ > 0) {
|
||||||
QBrush brush;
|
QBrush brush;
|
||||||
brush.setStyle(Qt::SolidPattern);
|
brush.setStyle(Qt::SolidPattern);
|
||||||
brush.setColor(bubbleBgColor());
|
if (unreadHighlightedMsgCount_ > 0) {
|
||||||
|
brush.setColor(mentionedColor());
|
||||||
|
} else {
|
||||||
|
brush.setColor(bubbleBgColor());
|
||||||
|
}
|
||||||
|
|
||||||
if (isPressed_)
|
if (isPressed_)
|
||||||
brush.setColor(bubbleFgColor());
|
brush.setColor(bubbleFgColor());
|
||||||
|
@ -354,9 +359,10 @@ RoomInfoListItem::paintEvent(QPaintEvent *event)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RoomInfoListItem::updateUnreadMessageCount(int count)
|
RoomInfoListItem::updateUnreadMessageCount(int count, int highlightedCount)
|
||||||
{
|
{
|
||||||
unreadMsgCount_ = count;
|
unreadMsgCount_ = count;
|
||||||
|
unreadHighlightedMsgCount_ = highlightedCount;
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,14 +59,15 @@ class RoomInfoListItem : public QWidget
|
||||||
Q_PROPERTY(QColor hoverTitleColor READ hoverTitleColor WRITE setHoverTitleColor)
|
Q_PROPERTY(QColor hoverTitleColor READ hoverTitleColor WRITE setHoverTitleColor)
|
||||||
Q_PROPERTY(QColor hoverSubtitleColor READ hoverSubtitleColor WRITE setHoverSubtitleColor)
|
Q_PROPERTY(QColor hoverSubtitleColor READ hoverSubtitleColor WRITE setHoverSubtitleColor)
|
||||||
|
|
||||||
|
Q_PROPERTY(QColor mentionedColor READ mentionedColor WRITE setMentionedColor)
|
||||||
Q_PROPERTY(QColor btnColor READ btnColor WRITE setBtnColor)
|
Q_PROPERTY(QColor btnColor READ btnColor WRITE setBtnColor)
|
||||||
Q_PROPERTY(QColor btnTextColor READ btnTextColor WRITE setBtnTextColor)
|
Q_PROPERTY(QColor btnTextColor READ btnTextColor WRITE setBtnTextColor)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RoomInfoListItem(QString room_id, RoomInfo info, QWidget *parent = 0);
|
RoomInfoListItem(QString room_id, RoomInfo info, QWidget *parent = 0);
|
||||||
|
|
||||||
void updateUnreadMessageCount(int count);
|
void updateUnreadMessageCount(int count, int highlightedCount);
|
||||||
void clearUnreadMessageCount() { updateUnreadMessageCount(0); };
|
void clearUnreadMessageCount() { updateUnreadMessageCount(0, 0); };
|
||||||
|
|
||||||
QString roomId() { return roomId_; }
|
QString roomId() { return roomId_; }
|
||||||
bool isPressed() const { return isPressed_; }
|
bool isPressed() const { return isPressed_; }
|
||||||
|
@ -97,6 +98,7 @@ public:
|
||||||
|
|
||||||
QColor bubbleFgColor() const { return bubbleFgColor_; }
|
QColor bubbleFgColor() const { return bubbleFgColor_; }
|
||||||
QColor bubbleBgColor() const { return bubbleBgColor_; }
|
QColor bubbleBgColor() const { return bubbleBgColor_; }
|
||||||
|
QColor mentionedColor() const { return mentionedFontColor_; }
|
||||||
|
|
||||||
void setHighlightedBackgroundColor(QColor &color) { highlightedBackgroundColor_ = color; }
|
void setHighlightedBackgroundColor(QColor &color) { highlightedBackgroundColor_ = color; }
|
||||||
void setHoverBackgroundColor(QColor &color) { hoverBackgroundColor_ = color; }
|
void setHoverBackgroundColor(QColor &color) { hoverBackgroundColor_ = color; }
|
||||||
|
@ -120,6 +122,7 @@ public:
|
||||||
|
|
||||||
void setBubbleFgColor(QColor &color) { bubbleFgColor_ = color; }
|
void setBubbleFgColor(QColor &color) { bubbleFgColor_ = color; }
|
||||||
void setBubbleBgColor(QColor &color) { bubbleBgColor_ = color; }
|
void setBubbleBgColor(QColor &color) { bubbleBgColor_ = color; }
|
||||||
|
void setMentionedColor(QColor &color) { mentionedFontColor_ = color; }
|
||||||
|
|
||||||
void setRoomName(const QString &name) { roomName_ = name; }
|
void setRoomName(const QString &name) { roomName_ = name; }
|
||||||
void setRoomType(bool isInvite)
|
void setRoomType(bool isInvite)
|
||||||
|
@ -184,7 +187,8 @@ private:
|
||||||
bool isPressed_ = false;
|
bool isPressed_ = false;
|
||||||
bool hasUnreadMessages_ = true;
|
bool hasUnreadMessages_ = true;
|
||||||
|
|
||||||
int unreadMsgCount_ = 0;
|
int unreadMsgCount_ = 0;
|
||||||
|
int unreadHighlightedMsgCount_ = 0;
|
||||||
|
|
||||||
QColor highlightedBackgroundColor_;
|
QColor highlightedBackgroundColor_;
|
||||||
QColor hoverBackgroundColor_;
|
QColor hoverBackgroundColor_;
|
||||||
|
@ -206,6 +210,7 @@ private:
|
||||||
QRectF declineBtnRegion_;
|
QRectF declineBtnRegion_;
|
||||||
|
|
||||||
// Fonts
|
// Fonts
|
||||||
|
QColor mentionedFontColor_;
|
||||||
QFont unreadCountFont_;
|
QFont unreadCountFont_;
|
||||||
int bubbleDiameter_;
|
int bubbleDiameter_;
|
||||||
|
|
||||||
|
|
|
@ -143,7 +143,7 @@ RoomList::removeRoom(const QString &room_id, bool reset)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RoomList::updateUnreadMessageCount(const QString &roomid, int count)
|
RoomList::updateUnreadMessageCount(const QString &roomid, int count, int highlightedCount)
|
||||||
{
|
{
|
||||||
if (!roomExists(roomid)) {
|
if (!roomExists(roomid)) {
|
||||||
nhlog::ui()->warn("updateUnreadMessageCount: unknown room_id {}",
|
nhlog::ui()->warn("updateUnreadMessageCount: unknown room_id {}",
|
||||||
|
@ -151,7 +151,7 @@ RoomList::updateUnreadMessageCount(const QString &roomid, int count)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rooms_[roomid]->updateUnreadMessageCount(count);
|
rooms_[roomid]->updateUnreadMessageCount(count, highlightedCount);
|
||||||
|
|
||||||
calculateUnreadMessageCount();
|
calculateUnreadMessageCount();
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ signals:
|
||||||
public slots:
|
public slots:
|
||||||
void updateRoomAvatar(const QString &roomid, const QPixmap &img);
|
void updateRoomAvatar(const QString &roomid, const QPixmap &img);
|
||||||
void highlightSelectedRoom(const QString &room_id);
|
void highlightSelectedRoom(const QString &room_id);
|
||||||
void updateUnreadMessageCount(const QString &roomid, int count);
|
void updateUnreadMessageCount(const QString &roomid, int count, int highlightedCount);
|
||||||
void updateRoomDescription(const QString &roomid, const DescInfo &info);
|
void updateRoomDescription(const QString &roomid, const DescInfo &info);
|
||||||
void closeJoinRoomDialog(bool isJoining, QString roomAlias);
|
void closeJoinRoomDialog(bool isJoining, QString roomAlias);
|
||||||
void updateReadStatus(const std::map<QString, bool> &status);
|
void updateReadStatus(const std::map<QString, bool> &status);
|
||||||
|
|
|
@ -513,8 +513,22 @@ TextInputWidget::TextInputWidget(QWidget *parent)
|
||||||
sendMessageBtn_->setIcon(send_message_icon);
|
sendMessageBtn_->setIcon(send_message_icon);
|
||||||
sendMessageBtn_->setIconSize(QSize(ButtonHeight, ButtonHeight));
|
sendMessageBtn_->setIconSize(QSize(ButtonHeight, ButtonHeight));
|
||||||
|
|
||||||
|
emojiBtn_ = new emoji::PickButton(this);
|
||||||
|
emojiBtn_->setToolTip(tr("Emoji"));
|
||||||
|
|
||||||
|
#if defined(Q_OS_MAC)
|
||||||
|
// macOS has a native emoji picker.
|
||||||
|
emojiBtn_->hide();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QIcon emoji_icon;
|
||||||
|
emoji_icon.addFile(":/icons/icons/ui/smile.png");
|
||||||
|
emojiBtn_->setIcon(emoji_icon);
|
||||||
|
emojiBtn_->setIconSize(QSize(ButtonHeight, ButtonHeight));
|
||||||
|
|
||||||
topLayout_->addWidget(sendFileBtn_);
|
topLayout_->addWidget(sendFileBtn_);
|
||||||
topLayout_->addWidget(input_);
|
topLayout_->addWidget(input_);
|
||||||
|
topLayout_->addWidget(emojiBtn_);
|
||||||
topLayout_->addWidget(sendMessageBtn_);
|
topLayout_->addWidget(sendMessageBtn_);
|
||||||
|
|
||||||
setLayout(topLayout_);
|
setLayout(topLayout_);
|
||||||
|
@ -527,6 +541,11 @@ TextInputWidget::TextInputWidget(QWidget *parent)
|
||||||
connect(input_, &FilteredTextEdit::audio, this, &TextInputWidget::uploadAudio);
|
connect(input_, &FilteredTextEdit::audio, this, &TextInputWidget::uploadAudio);
|
||||||
connect(input_, &FilteredTextEdit::video, this, &TextInputWidget::uploadVideo);
|
connect(input_, &FilteredTextEdit::video, this, &TextInputWidget::uploadVideo);
|
||||||
connect(input_, &FilteredTextEdit::file, this, &TextInputWidget::uploadFile);
|
connect(input_, &FilteredTextEdit::file, this, &TextInputWidget::uploadFile);
|
||||||
|
connect(emojiBtn_,
|
||||||
|
SIGNAL(emojiSelected(const QString &)),
|
||||||
|
this,
|
||||||
|
SLOT(addSelectedEmoji(const QString &)));
|
||||||
|
|
||||||
connect(input_, &FilteredTextEdit::startedTyping, this, &TextInputWidget::startedTyping);
|
connect(input_, &FilteredTextEdit::startedTyping, this, &TextInputWidget::startedTyping);
|
||||||
|
|
||||||
connect(input_, &FilteredTextEdit::stoppedTyping, this, &TextInputWidget::stoppedTyping);
|
connect(input_, &FilteredTextEdit::stoppedTyping, this, &TextInputWidget::stoppedTyping);
|
||||||
|
@ -535,6 +554,22 @@ TextInputWidget::TextInputWidget(QWidget *parent)
|
||||||
input_, &FilteredTextEdit::startedUpload, this, &TextInputWidget::showUploadSpinner);
|
input_, &FilteredTextEdit::startedUpload, this, &TextInputWidget::showUploadSpinner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TextInputWidget::addSelectedEmoji(const QString &emoji)
|
||||||
|
{
|
||||||
|
QTextCursor cursor = input_->textCursor();
|
||||||
|
|
||||||
|
QTextCharFormat charfmt;
|
||||||
|
input_->setCurrentCharFormat(charfmt);
|
||||||
|
|
||||||
|
input_->insertPlainText(emoji);
|
||||||
|
cursor.movePosition(QTextCursor::End);
|
||||||
|
|
||||||
|
input_->setCurrentCharFormat(charfmt);
|
||||||
|
|
||||||
|
input_->show();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TextInputWidget::command(QString command, QString args)
|
TextInputWidget::command(QString command, QString args)
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#include "SuggestionsPopup.h"
|
#include "SuggestionsPopup.h"
|
||||||
#include "dialogs/PreviewUploadOverlay.h"
|
#include "dialogs/PreviewUploadOverlay.h"
|
||||||
|
#include "emoji/PickButton.h"
|
||||||
|
|
||||||
namespace dialogs {
|
namespace dialogs {
|
||||||
class PreviewUploadOverlay;
|
class PreviewUploadOverlay;
|
||||||
|
@ -159,6 +160,9 @@ public slots:
|
||||||
void focusLineEdit() { input_->setFocus(); }
|
void focusLineEdit() { input_->setFocus(); }
|
||||||
void addReply(const QString &username, const QString &msg);
|
void addReply(const QString &username, const QString &msg);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void addSelectedEmoji(const QString &emoji);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void sendTextMessage(QString msg);
|
void sendTextMessage(QString msg);
|
||||||
void sendEmoteMessage(QString msg);
|
void sendEmoteMessage(QString msg);
|
||||||
|
@ -189,6 +193,7 @@ private:
|
||||||
|
|
||||||
FlatButton *sendFileBtn_;
|
FlatButton *sendFileBtn_;
|
||||||
FlatButton *sendMessageBtn_;
|
FlatButton *sendMessageBtn_;
|
||||||
|
emoji::PickButton *emojiBtn_;
|
||||||
|
|
||||||
QColor borderColor_;
|
QColor borderColor_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -49,7 +49,7 @@ UserSettings::load()
|
||||||
isTypingNotificationsEnabled_ = settings.value("user/typing_notifications", true).toBool();
|
isTypingNotificationsEnabled_ = settings.value("user/typing_notifications", true).toBool();
|
||||||
isReadReceiptsEnabled_ = settings.value("user/read_receipts", true).toBool();
|
isReadReceiptsEnabled_ = settings.value("user/read_receipts", true).toBool();
|
||||||
theme_ = settings.value("user/theme", "light").toString();
|
theme_ = settings.value("user/theme", "light").toString();
|
||||||
|
font_ = settings.value("user/font_family", "default").toString();
|
||||||
baseFontSize_ = settings.value("user/font_size", QFont().pointSizeF()).toDouble();
|
baseFontSize_ = settings.value("user/font_size", QFont().pointSizeF()).toDouble();
|
||||||
|
|
||||||
applyTheme();
|
applyTheme();
|
||||||
|
@ -62,6 +62,13 @@ UserSettings::setFontSize(double size)
|
||||||
save();
|
save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
UserSettings::setFontFamily(QString family)
|
||||||
|
{
|
||||||
|
font_ = family;
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
UserSettings::setTheme(QString theme)
|
UserSettings::setTheme(QString theme)
|
||||||
{
|
{
|
||||||
|
@ -106,6 +113,7 @@ UserSettings::save()
|
||||||
settings.setValue("group_view", isGroupViewEnabled_);
|
settings.setValue("group_view", isGroupViewEnabled_);
|
||||||
settings.setValue("desktop_notifications", hasDesktopNotifications_);
|
settings.setValue("desktop_notifications", hasDesktopNotifications_);
|
||||||
settings.setValue("theme", theme());
|
settings.setValue("theme", theme());
|
||||||
|
settings.setValue("font_family", font_);
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,6 +228,23 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
|
||||||
fontSizeOptionLayout->addWidget(fontSizeLabel);
|
fontSizeOptionLayout->addWidget(fontSizeLabel);
|
||||||
fontSizeOptionLayout->addWidget(fontSizeCombo_, 0, Qt::AlignRight);
|
fontSizeOptionLayout->addWidget(fontSizeCombo_, 0, Qt::AlignRight);
|
||||||
|
|
||||||
|
auto fontFamilyOptionLayout = new QHBoxLayout;
|
||||||
|
fontFamilyOptionLayout->setContentsMargins(0, OptionMargin, 0, OptionMargin);
|
||||||
|
auto fontFamilyLabel = new QLabel(tr("Font Family"), this);
|
||||||
|
fontFamilyLabel->setFont(font);
|
||||||
|
fontSelectionCombo_ = new QComboBox(this);
|
||||||
|
QFontDatabase fontDb;
|
||||||
|
auto fontFamilies = fontDb.families();
|
||||||
|
for (const auto &family : fontFamilies) {
|
||||||
|
fontSelectionCombo_->addItem(family);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fontIndex = fontSelectionCombo_->findText(settings_->font());
|
||||||
|
fontSelectionCombo_->setCurrentIndex(fontIndex);
|
||||||
|
|
||||||
|
fontFamilyOptionLayout->addWidget(fontFamilyLabel);
|
||||||
|
fontFamilyOptionLayout->addWidget(fontSelectionCombo_, 0, Qt::AlignRight);
|
||||||
|
|
||||||
auto themeOptionLayout_ = new QHBoxLayout;
|
auto themeOptionLayout_ = new QHBoxLayout;
|
||||||
themeOptionLayout_->setContentsMargins(0, OptionMargin, 0, OptionMargin);
|
themeOptionLayout_->setContentsMargins(0, OptionMargin, 0, OptionMargin);
|
||||||
auto themeLabel_ = new QLabel(tr("Theme"), this);
|
auto themeLabel_ = new QLabel(tr("Theme"), this);
|
||||||
|
@ -229,6 +254,11 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
|
||||||
themeCombo_->addItem("Dark");
|
themeCombo_->addItem("Dark");
|
||||||
themeCombo_->addItem("System");
|
themeCombo_->addItem("System");
|
||||||
|
|
||||||
|
QString themeStr = settings_->theme();
|
||||||
|
themeStr.replace(0, 1, themeStr[0].toUpper());
|
||||||
|
int themeIndex = themeCombo_->findText(themeStr);
|
||||||
|
themeCombo_->setCurrentIndex(themeIndex);
|
||||||
|
|
||||||
themeOptionLayout_->addWidget(themeLabel_);
|
themeOptionLayout_->addWidget(themeLabel_);
|
||||||
themeOptionLayout_->addWidget(themeCombo_, 0, Qt::AlignRight);
|
themeOptionLayout_->addWidget(themeCombo_, 0, Qt::AlignRight);
|
||||||
|
|
||||||
|
@ -319,6 +349,7 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
|
||||||
|
|
||||||
mainLayout_->addLayout(scaleFactorOptionLayout);
|
mainLayout_->addLayout(scaleFactorOptionLayout);
|
||||||
mainLayout_->addLayout(fontSizeOptionLayout);
|
mainLayout_->addLayout(fontSizeOptionLayout);
|
||||||
|
mainLayout_->addLayout(fontFamilyOptionLayout);
|
||||||
mainLayout_->addWidget(new HorizontalLine(this));
|
mainLayout_->addWidget(new HorizontalLine(this));
|
||||||
mainLayout_->addLayout(themeOptionLayout_);
|
mainLayout_->addLayout(themeOptionLayout_);
|
||||||
mainLayout_->addWidget(new HorizontalLine(this));
|
mainLayout_->addWidget(new HorizontalLine(this));
|
||||||
|
@ -348,14 +379,19 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
|
||||||
|
|
||||||
connect(themeCombo_,
|
connect(themeCombo_,
|
||||||
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated),
|
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated),
|
||||||
[this](const QString &text) { settings_->setTheme(text.toLower()); });
|
[this](const QString &text) {
|
||||||
|
settings_->setTheme(text.toLower());
|
||||||
|
emit themeChanged();
|
||||||
|
});
|
||||||
connect(scaleFactorCombo_,
|
connect(scaleFactorCombo_,
|
||||||
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated),
|
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated),
|
||||||
[](const QString &factor) { utils::setScaleFactor(factor.toFloat()); });
|
[](const QString &factor) { utils::setScaleFactor(factor.toFloat()); });
|
||||||
connect(fontSizeCombo_,
|
connect(fontSizeCombo_,
|
||||||
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated),
|
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated),
|
||||||
[this](const QString &size) { settings_->setFontSize(size.trimmed().toDouble()); });
|
[this](const QString &size) { settings_->setFontSize(size.trimmed().toDouble()); });
|
||||||
|
connect(fontSelectionCombo_,
|
||||||
|
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated),
|
||||||
|
[this](const QString &family) { settings_->setFontFamily(family.trimmed()); });
|
||||||
connect(trayToggle_, &Toggle::toggled, this, [this](bool isDisabled) {
|
connect(trayToggle_, &Toggle::toggled, this, [this](bool isDisabled) {
|
||||||
settings_->setTray(!isDisabled);
|
settings_->setTray(!isDisabled);
|
||||||
if (isDisabled) {
|
if (isDisabled) {
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
|
#include <QFontDatabase>
|
||||||
#include <QFrame>
|
#include <QFrame>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QLayout>
|
#include <QLayout>
|
||||||
|
@ -54,6 +55,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void setFontSize(double size);
|
void setFontSize(double size);
|
||||||
|
void setFontFamily(QString family);
|
||||||
|
|
||||||
void setGroupView(bool state)
|
void setGroupView(bool state)
|
||||||
{
|
{
|
||||||
|
@ -90,6 +92,7 @@ public:
|
||||||
bool isReadReceiptsEnabled() const { return isReadReceiptsEnabled_; }
|
bool isReadReceiptsEnabled() const { return isReadReceiptsEnabled_; }
|
||||||
bool hasDesktopNotifications() const { return hasDesktopNotifications_; }
|
bool hasDesktopNotifications() const { return hasDesktopNotifications_; }
|
||||||
double fontSize() const { return baseFontSize_; }
|
double fontSize() const { return baseFontSize_; }
|
||||||
|
QString font() const { return font_; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void groupViewStateChanged(bool state);
|
void groupViewStateChanged(bool state);
|
||||||
|
@ -103,6 +106,7 @@ private:
|
||||||
bool isReadReceiptsEnabled_;
|
bool isReadReceiptsEnabled_;
|
||||||
bool hasDesktopNotifications_;
|
bool hasDesktopNotifications_;
|
||||||
double baseFontSize_;
|
double baseFontSize_;
|
||||||
|
QString font_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HorizontalLine : public QFrame
|
class HorizontalLine : public QFrame
|
||||||
|
@ -128,6 +132,7 @@ protected:
|
||||||
signals:
|
signals:
|
||||||
void moveBack();
|
void moveBack();
|
||||||
void trayOptionChanged(bool value);
|
void trayOptionChanged(bool value);
|
||||||
|
void themeChanged();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void importSessionKeys();
|
void importSessionKeys();
|
||||||
|
@ -154,6 +159,7 @@ private:
|
||||||
QComboBox *themeCombo_;
|
QComboBox *themeCombo_;
|
||||||
QComboBox *scaleFactorCombo_;
|
QComboBox *scaleFactorCombo_;
|
||||||
QComboBox *fontSizeCombo_;
|
QComboBox *fontSizeCombo_;
|
||||||
|
QComboBox *fontSelectionCombo_;
|
||||||
|
|
||||||
int sideMargin_ = 0;
|
int sideMargin_ = 0;
|
||||||
};
|
};
|
||||||
|
|
122
src/Utils.cpp
|
@ -15,6 +15,8 @@
|
||||||
|
|
||||||
using TimelineEvent = mtx::events::collections::TimelineEvents;
|
using TimelineEvent = mtx::events::collections::TimelineEvents;
|
||||||
|
|
||||||
|
QHash<QString, QString> authorColors_;
|
||||||
|
|
||||||
QString
|
QString
|
||||||
utils::localUser()
|
utils::localUser()
|
||||||
{
|
{
|
||||||
|
@ -382,6 +384,126 @@ utils::linkColor()
|
||||||
return QPalette().color(QPalette::Link).name();
|
return QPalette().color(QPalette::Link).name();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
utils::hashQString(const QString &input)
|
||||||
|
{
|
||||||
|
auto hash = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < input.length(); i++) {
|
||||||
|
hash = input.at(i).digitValue() + ((hash << 5) - hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
utils::generateContrastingHexColor(const QString &input, const QString &background)
|
||||||
|
{
|
||||||
|
const QColor backgroundCol(background);
|
||||||
|
const qreal backgroundLum = luminance(background);
|
||||||
|
|
||||||
|
// Create a color for the input
|
||||||
|
auto hash = hashQString(input);
|
||||||
|
// create a hue value based on the hash of the input.
|
||||||
|
auto userHue = qAbs(hash % 360);
|
||||||
|
// start with moderate saturation and lightness values.
|
||||||
|
auto sat = 220;
|
||||||
|
auto lightness = 125;
|
||||||
|
|
||||||
|
// converting to a QColor makes the luminance calc easier.
|
||||||
|
QColor inputColor = QColor::fromHsl(userHue, sat, lightness);
|
||||||
|
|
||||||
|
// calculate the initial luminance and contrast of the
|
||||||
|
// generated color. It's possible that no additional
|
||||||
|
// work will be necessary.
|
||||||
|
auto lum = luminance(inputColor);
|
||||||
|
auto contrast = computeContrast(lum, backgroundLum);
|
||||||
|
|
||||||
|
// If the contrast doesn't meet our criteria,
|
||||||
|
// try again and again until they do by modifying first
|
||||||
|
// the lightness and then the saturation of the color.
|
||||||
|
while (contrast < 5) {
|
||||||
|
// if our lightness is at it's bounds, try changing
|
||||||
|
// saturation instead.
|
||||||
|
if (lightness == 242 || lightness == 13) {
|
||||||
|
qreal newSat = qBound(26.0, sat * 1.25, 242.0);
|
||||||
|
|
||||||
|
inputColor.setHsl(userHue, qFloor(newSat), lightness);
|
||||||
|
auto tmpLum = luminance(inputColor);
|
||||||
|
auto higherContrast = computeContrast(tmpLum, backgroundLum);
|
||||||
|
if (higherContrast > contrast) {
|
||||||
|
contrast = higherContrast;
|
||||||
|
sat = newSat;
|
||||||
|
} else {
|
||||||
|
newSat = qBound(26.0, sat / 1.25, 242.0);
|
||||||
|
inputColor.setHsl(userHue, qFloor(newSat), lightness);
|
||||||
|
tmpLum = luminance(inputColor);
|
||||||
|
auto lowerContrast = computeContrast(tmpLum, backgroundLum);
|
||||||
|
if (lowerContrast > contrast) {
|
||||||
|
contrast = lowerContrast;
|
||||||
|
sat = newSat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qreal newLightness = qBound(13.0, lightness * 1.25, 242.0);
|
||||||
|
|
||||||
|
inputColor.setHsl(userHue, sat, qFloor(newLightness));
|
||||||
|
|
||||||
|
auto tmpLum = luminance(inputColor);
|
||||||
|
auto higherContrast = computeContrast(tmpLum, backgroundLum);
|
||||||
|
|
||||||
|
// Check to make sure we have actually improved contrast
|
||||||
|
if (higherContrast > contrast) {
|
||||||
|
contrast = higherContrast;
|
||||||
|
lightness = newLightness;
|
||||||
|
// otherwise, try going the other way instead.
|
||||||
|
} else {
|
||||||
|
newLightness = qBound(13.0, lightness / 1.25, 242.0);
|
||||||
|
inputColor.setHsl(userHue, sat, qFloor(newLightness));
|
||||||
|
tmpLum = luminance(inputColor);
|
||||||
|
auto lowerContrast = computeContrast(tmpLum, backgroundLum);
|
||||||
|
if (lowerContrast > contrast) {
|
||||||
|
contrast = lowerContrast;
|
||||||
|
lightness = newLightness;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the hex value of the generated color.
|
||||||
|
auto colorHex = inputColor.name();
|
||||||
|
|
||||||
|
return colorHex;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal
|
||||||
|
utils::computeContrast(const qreal &one, const qreal &two)
|
||||||
|
{
|
||||||
|
auto ratio = (one + 0.05) / (two + 0.05);
|
||||||
|
|
||||||
|
if (two > one) {
|
||||||
|
ratio = 1 / ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal
|
||||||
|
utils::luminance(const QColor &col)
|
||||||
|
{
|
||||||
|
int colRgb[3] = {col.red(), col.green(), col.blue()};
|
||||||
|
qreal lumRgb[3];
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
qreal v = colRgb[i] / 255.0;
|
||||||
|
v <= 0.03928 ? lumRgb[i] = v / 12.92 : lumRgb[i] = qPow((v + 0.055) / 1.055, 2.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lum = lumRgb[0] * 0.2126 + lumRgb[1] * 0.7152 + lumRgb[2] * 0.0722;
|
||||||
|
|
||||||
|
return lum;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
utils::centerWidget(QWidget *widget, QWidget *parent)
|
utils::centerWidget(QWidget *widget, QWidget *parent)
|
||||||
{
|
{
|
||||||
|
|
19
src/Utils.h
|
@ -14,6 +14,8 @@
|
||||||
#include <mtx/events/collections.hpp>
|
#include <mtx/events/collections.hpp>
|
||||||
#include <mtx/events/common.hpp>
|
#include <mtx/events/common.hpp>
|
||||||
|
|
||||||
|
#include <qmath.h>
|
||||||
|
|
||||||
class QComboBox;
|
class QComboBox;
|
||||||
|
|
||||||
namespace utils {
|
namespace utils {
|
||||||
|
@ -227,6 +229,23 @@ markdownToHtml(const QString &text);
|
||||||
QString
|
QString
|
||||||
linkColor();
|
linkColor();
|
||||||
|
|
||||||
|
//! Returns the hash code of the input QString
|
||||||
|
int
|
||||||
|
hashQString(const QString &input);
|
||||||
|
|
||||||
|
//! Generate a color (matching #RRGGBB) that has an acceptable contrast to background that is based
|
||||||
|
//! on the input string.
|
||||||
|
QString
|
||||||
|
generateContrastingHexColor(const QString &input, const QString &background);
|
||||||
|
|
||||||
|
//! Given two luminance values, compute the contrast ratio between them.
|
||||||
|
qreal
|
||||||
|
computeContrast(const qreal &one, const qreal &two);
|
||||||
|
|
||||||
|
//! Compute the luminance of a single color. Based on https://stackoverflow.com/a/9733420
|
||||||
|
qreal
|
||||||
|
luminance(const QColor &col);
|
||||||
|
|
||||||
//! Center a widget in relation to another widget.
|
//! Center a widget in relation to another widget.
|
||||||
void
|
void
|
||||||
centerWidget(QWidget *widget, QWidget *parent);
|
centerWidget(QWidget *widget, QWidget *parent);
|
||||||
|
|
|
@ -76,6 +76,8 @@ ImageOverlay::paintEvent(QPaintEvent *event)
|
||||||
content_ = QRect(outer_margin + diff_x / 2, diff_y / 2, image_.width(), image_.height());
|
content_ = QRect(outer_margin + diff_x / 2, diff_y / 2, image_.width(), image_.height());
|
||||||
close_button_ =
|
close_button_ =
|
||||||
QRect(screen_.width() - margin - buttonSize, margin, buttonSize, buttonSize);
|
QRect(screen_.width() - margin - buttonSize, margin, buttonSize, buttonSize);
|
||||||
|
save_button_ =
|
||||||
|
QRect(screen_.width() - (2 * margin) - (2 * buttonSize), margin, buttonSize, buttonSize);
|
||||||
|
|
||||||
// Draw main content_.
|
// Draw main content_.
|
||||||
painter.drawPixmap(content_, image_);
|
painter.drawPixmap(content_, image_);
|
||||||
|
@ -91,6 +93,12 @@ ImageOverlay::paintEvent(QPaintEvent *event)
|
||||||
painter.setPen(pen);
|
painter.setPen(pen);
|
||||||
painter.drawLine(center - QPointF(15, 15), center + QPointF(15, 15));
|
painter.drawLine(center - QPointF(15, 15), center + QPointF(15, 15));
|
||||||
painter.drawLine(center + QPointF(15, -15), center - QPointF(15, -15));
|
painter.drawLine(center + QPointF(15, -15), center - QPointF(15, -15));
|
||||||
|
|
||||||
|
// Draw download button
|
||||||
|
center = save_button_.center();
|
||||||
|
painter.drawLine(center - QPointF(0, 15), center + QPointF(0, 15));
|
||||||
|
painter.drawLine(center - QPointF(15, 0), center + QPointF(0, 15));
|
||||||
|
painter.drawLine(center + QPointF(0, 15), center + QPointF(15, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -101,6 +109,8 @@ ImageOverlay::mousePressEvent(QMouseEvent *event)
|
||||||
|
|
||||||
if (close_button_.contains(event->pos()))
|
if (close_button_.contains(event->pos()))
|
||||||
emit closing();
|
emit closing();
|
||||||
|
else if (save_button_.contains(event->pos()))
|
||||||
|
emit saving();
|
||||||
else if (!content_.contains(event->pos()))
|
else if (!content_.contains(event->pos()))
|
||||||
emit closing();
|
emit closing();
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ protected:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void closing();
|
void closing();
|
||||||
|
void saving();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPixmap originalImage_;
|
QPixmap originalImage_;
|
||||||
|
@ -42,6 +43,7 @@ private:
|
||||||
|
|
||||||
QRect content_;
|
QRect content_;
|
||||||
QRect close_button_;
|
QRect close_button_;
|
||||||
|
QRect save_button_;
|
||||||
QRect screen_;
|
QRect screen_;
|
||||||
};
|
};
|
||||||
} // dialogs
|
} // dialogs
|
||||||
|
|
90
src/emoji/Category.cpp
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QScrollBar>
|
||||||
|
#include <QStyleOption>
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#include "emoji/Category.h"
|
||||||
|
|
||||||
|
using namespace emoji;
|
||||||
|
|
||||||
|
Category::Category(QString category, std::vector<Emoji> emoji, QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
mainLayout_ = new QVBoxLayout(this);
|
||||||
|
mainLayout_->setMargin(0);
|
||||||
|
mainLayout_->setSpacing(0);
|
||||||
|
|
||||||
|
emojiListView_ = new QListView();
|
||||||
|
itemModel_ = new QStandardItemModel(this);
|
||||||
|
|
||||||
|
delegate_ = new ItemDelegate(this);
|
||||||
|
data_ = new Emoji;
|
||||||
|
|
||||||
|
emojiListView_->setItemDelegate(delegate_);
|
||||||
|
emojiListView_->setModel(itemModel_);
|
||||||
|
emojiListView_->setViewMode(QListView::IconMode);
|
||||||
|
emojiListView_->setFlow(QListView::LeftToRight);
|
||||||
|
emojiListView_->setResizeMode(QListView::Adjust);
|
||||||
|
emojiListView_->verticalScrollBar()->setEnabled(false);
|
||||||
|
emojiListView_->horizontalScrollBar()->setEnabled(false);
|
||||||
|
|
||||||
|
const int cols = 7;
|
||||||
|
const int rows = emoji.size() / 7;
|
||||||
|
|
||||||
|
// TODO: Be precise here. Take the parent into consideration.
|
||||||
|
emojiListView_->setFixedSize(cols * 50 + 20, rows * 50 + 20);
|
||||||
|
emojiListView_->setGridSize(QSize(50, 50));
|
||||||
|
emojiListView_->setDragEnabled(false);
|
||||||
|
emojiListView_->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
|
||||||
|
for (const auto &e : emoji) {
|
||||||
|
data_->unicode = e.unicode;
|
||||||
|
|
||||||
|
auto item = new QStandardItem;
|
||||||
|
item->setSizeHint(QSize(24, 24));
|
||||||
|
|
||||||
|
QVariant unicode(data_->unicode);
|
||||||
|
item->setData(unicode.toString(), Qt::UserRole);
|
||||||
|
|
||||||
|
itemModel_->appendRow(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
QFont font;
|
||||||
|
font.setWeight(QFont::Medium);
|
||||||
|
|
||||||
|
category_ = new QLabel(category, this);
|
||||||
|
category_->setFont(font);
|
||||||
|
category_->setStyleSheet("margin: 20px 0 20px 8px;");
|
||||||
|
|
||||||
|
mainLayout_->addWidget(category_);
|
||||||
|
mainLayout_->addWidget(emojiListView_);
|
||||||
|
|
||||||
|
connect(emojiListView_, &QListView::clicked, this, &Category::clickIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Category::paintEvent(QPaintEvent *)
|
||||||
|
{
|
||||||
|
QStyleOption opt;
|
||||||
|
opt.init(this);
|
||||||
|
QPainter p(this);
|
||||||
|
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
||||||
|
}
|
59
src/emoji/Category.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QLayout>
|
||||||
|
#include <QListView>
|
||||||
|
#include <QStandardItemModel>
|
||||||
|
|
||||||
|
#include "ItemDelegate.h"
|
||||||
|
|
||||||
|
namespace emoji {
|
||||||
|
|
||||||
|
class Category : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
Category(QString category, std::vector<Emoji> emoji, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void emojiSelected(const QString &emoji);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void clickIndex(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
emit emojiSelected(index.data(Qt::UserRole).toString());
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVBoxLayout *mainLayout_;
|
||||||
|
|
||||||
|
QStandardItemModel *itemModel_;
|
||||||
|
QListView *emojiListView_;
|
||||||
|
|
||||||
|
emoji::Emoji *data_;
|
||||||
|
emoji::ItemDelegate *delegate_;
|
||||||
|
|
||||||
|
QLabel *category_;
|
||||||
|
};
|
||||||
|
} // namespace emoji
|
48
src/emoji/ItemDelegate.cpp
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
|
#include "emoji/ItemDelegate.h"
|
||||||
|
|
||||||
|
using namespace emoji;
|
||||||
|
|
||||||
|
ItemDelegate::ItemDelegate(QObject *parent)
|
||||||
|
: QStyledItemDelegate(parent)
|
||||||
|
{
|
||||||
|
data_ = new Emoji;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemDelegate::~ItemDelegate() { delete data_; }
|
||||||
|
|
||||||
|
void
|
||||||
|
ItemDelegate::paint(QPainter *painter,
|
||||||
|
const QStyleOptionViewItem &option,
|
||||||
|
const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(index);
|
||||||
|
|
||||||
|
QStyleOptionViewItem viewOption(option);
|
||||||
|
|
||||||
|
auto emoji = index.data(Qt::UserRole).toString();
|
||||||
|
|
||||||
|
// QFont font("Emoji One");
|
||||||
|
QFont font;
|
||||||
|
painter->setFont(font);
|
||||||
|
painter->drawText(viewOption.rect, Qt::AlignCenter, emoji);
|
||||||
|
}
|
43
src/emoji/ItemDelegate.h
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QModelIndex>
|
||||||
|
#include <QStandardItemModel>
|
||||||
|
#include <QStyledItemDelegate>
|
||||||
|
|
||||||
|
#include "Provider.h"
|
||||||
|
|
||||||
|
namespace emoji {
|
||||||
|
|
||||||
|
class ItemDelegate : public QStyledItemDelegate
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ItemDelegate(QObject *parent = nullptr);
|
||||||
|
~ItemDelegate();
|
||||||
|
|
||||||
|
void paint(QPainter *painter,
|
||||||
|
const QStyleOptionViewItem &option,
|
||||||
|
const QModelIndex &index) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Emoji *data_;
|
||||||
|
};
|
||||||
|
} // namespace emoji
|
236
src/emoji/Panel.cpp
Normal file
|
@ -0,0 +1,236 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QScrollBar>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
#include "ui/DropShadow.h"
|
||||||
|
#include "ui/FlatButton.h"
|
||||||
|
|
||||||
|
#include "emoji/Category.h"
|
||||||
|
#include "emoji/Panel.h"
|
||||||
|
|
||||||
|
using namespace emoji;
|
||||||
|
|
||||||
|
Panel::Panel(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, shadowMargin_{2}
|
||||||
|
, width_{370}
|
||||||
|
, height_{350}
|
||||||
|
, categoryIconSize_{20}
|
||||||
|
{
|
||||||
|
setStyleSheet("QWidget {border: none;}"
|
||||||
|
"QScrollBar:vertical { width: 0px; margin: 0px; }"
|
||||||
|
"QScrollBar::handle:vertical { min-height: 30px; }");
|
||||||
|
|
||||||
|
setAttribute(Qt::WA_ShowWithoutActivating, true);
|
||||||
|
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint);
|
||||||
|
|
||||||
|
auto mainWidget = new QWidget(this);
|
||||||
|
mainWidget->setMaximumSize(width_, height_);
|
||||||
|
|
||||||
|
auto topLayout = new QVBoxLayout(this);
|
||||||
|
topLayout->addWidget(mainWidget);
|
||||||
|
topLayout->setMargin(shadowMargin_);
|
||||||
|
topLayout->setSpacing(0);
|
||||||
|
|
||||||
|
auto contentLayout = new QVBoxLayout(mainWidget);
|
||||||
|
contentLayout->setMargin(0);
|
||||||
|
contentLayout->setSpacing(0);
|
||||||
|
|
||||||
|
auto emojiCategories = new QFrame(mainWidget);
|
||||||
|
|
||||||
|
auto categoriesLayout = new QHBoxLayout(emojiCategories);
|
||||||
|
categoriesLayout->setSpacing(0);
|
||||||
|
categoriesLayout->setMargin(0);
|
||||||
|
|
||||||
|
QIcon icon;
|
||||||
|
|
||||||
|
auto peopleCategory = new FlatButton(emojiCategories);
|
||||||
|
icon.addFile(":/icons/icons/emoji-categories/people.png");
|
||||||
|
peopleCategory->setIcon(icon);
|
||||||
|
peopleCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_));
|
||||||
|
|
||||||
|
auto natureCategory_ = new FlatButton(emojiCategories);
|
||||||
|
icon.addFile(":/icons/icons/emoji-categories/nature.png");
|
||||||
|
natureCategory_->setIcon(icon);
|
||||||
|
natureCategory_->setIconSize(QSize(categoryIconSize_, categoryIconSize_));
|
||||||
|
|
||||||
|
auto foodCategory_ = new FlatButton(emojiCategories);
|
||||||
|
icon.addFile(":/icons/icons/emoji-categories/foods.png");
|
||||||
|
foodCategory_->setIcon(icon);
|
||||||
|
foodCategory_->setIconSize(QSize(categoryIconSize_, categoryIconSize_));
|
||||||
|
|
||||||
|
auto activityCategory = new FlatButton(emojiCategories);
|
||||||
|
icon.addFile(":/icons/icons/emoji-categories/activity.png");
|
||||||
|
activityCategory->setIcon(icon);
|
||||||
|
activityCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_));
|
||||||
|
|
||||||
|
auto travelCategory = new FlatButton(emojiCategories);
|
||||||
|
icon.addFile(":/icons/icons/emoji-categories/travel.png");
|
||||||
|
travelCategory->setIcon(icon);
|
||||||
|
travelCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_));
|
||||||
|
|
||||||
|
auto objectsCategory = new FlatButton(emojiCategories);
|
||||||
|
icon.addFile(":/icons/icons/emoji-categories/objects.png");
|
||||||
|
objectsCategory->setIcon(icon);
|
||||||
|
objectsCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_));
|
||||||
|
|
||||||
|
auto symbolsCategory = new FlatButton(emojiCategories);
|
||||||
|
icon.addFile(":/icons/icons/emoji-categories/symbols.png");
|
||||||
|
symbolsCategory->setIcon(icon);
|
||||||
|
symbolsCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_));
|
||||||
|
|
||||||
|
auto flagsCategory = new FlatButton(emojiCategories);
|
||||||
|
icon.addFile(":/icons/icons/emoji-categories/flags.png");
|
||||||
|
flagsCategory->setIcon(icon);
|
||||||
|
flagsCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_));
|
||||||
|
|
||||||
|
categoriesLayout->addWidget(peopleCategory);
|
||||||
|
categoriesLayout->addWidget(natureCategory_);
|
||||||
|
categoriesLayout->addWidget(foodCategory_);
|
||||||
|
categoriesLayout->addWidget(activityCategory);
|
||||||
|
categoriesLayout->addWidget(travelCategory);
|
||||||
|
categoriesLayout->addWidget(objectsCategory);
|
||||||
|
categoriesLayout->addWidget(symbolsCategory);
|
||||||
|
categoriesLayout->addWidget(flagsCategory);
|
||||||
|
|
||||||
|
scrollArea_ = new QScrollArea(this);
|
||||||
|
scrollArea_->setWidgetResizable(true);
|
||||||
|
scrollArea_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
|
||||||
|
auto scrollWidget = new QWidget(this);
|
||||||
|
auto scrollLayout = new QVBoxLayout(scrollWidget);
|
||||||
|
|
||||||
|
scrollLayout->setMargin(0);
|
||||||
|
scrollLayout->setSpacing(0);
|
||||||
|
scrollArea_->setWidget(scrollWidget);
|
||||||
|
|
||||||
|
auto peopleEmoji =
|
||||||
|
new Category(tr("Smileys & People"), emoji_provider_.people, scrollWidget);
|
||||||
|
scrollLayout->addWidget(peopleEmoji);
|
||||||
|
|
||||||
|
auto natureEmoji =
|
||||||
|
new Category(tr("Animals & Nature"), emoji_provider_.nature, scrollWidget);
|
||||||
|
scrollLayout->addWidget(natureEmoji);
|
||||||
|
|
||||||
|
auto foodEmoji = new Category(tr("Food & Drink"), emoji_provider_.food, scrollWidget);
|
||||||
|
scrollLayout->addWidget(foodEmoji);
|
||||||
|
|
||||||
|
auto activityEmoji = new Category(tr("Activity"), emoji_provider_.activity, scrollWidget);
|
||||||
|
scrollLayout->addWidget(activityEmoji);
|
||||||
|
|
||||||
|
auto travelEmoji =
|
||||||
|
new Category(tr("Travel & Places"), emoji_provider_.travel, scrollWidget);
|
||||||
|
scrollLayout->addWidget(travelEmoji);
|
||||||
|
|
||||||
|
auto objectsEmoji = new Category(tr("Objects"), emoji_provider_.objects, scrollWidget);
|
||||||
|
scrollLayout->addWidget(objectsEmoji);
|
||||||
|
|
||||||
|
auto symbolsEmoji = new Category(tr("Symbols"), emoji_provider_.symbols, scrollWidget);
|
||||||
|
scrollLayout->addWidget(symbolsEmoji);
|
||||||
|
|
||||||
|
auto flagsEmoji = new Category(tr("Flags"), emoji_provider_.flags, scrollWidget);
|
||||||
|
scrollLayout->addWidget(flagsEmoji);
|
||||||
|
|
||||||
|
contentLayout->addWidget(scrollArea_);
|
||||||
|
contentLayout->addWidget(emojiCategories);
|
||||||
|
|
||||||
|
connect(peopleEmoji, &Category::emojiSelected, this, &Panel::emojiSelected);
|
||||||
|
connect(peopleCategory, &QPushButton::clicked, [this, peopleEmoji]() {
|
||||||
|
this->showCategory(peopleEmoji);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(natureEmoji, &Category::emojiSelected, this, &Panel::emojiSelected);
|
||||||
|
connect(natureCategory_, &QPushButton::clicked, [this, natureEmoji]() {
|
||||||
|
this->showCategory(natureEmoji);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(foodEmoji, &Category::emojiSelected, this, &Panel::emojiSelected);
|
||||||
|
connect(foodCategory_, &QPushButton::clicked, [this, foodEmoji]() {
|
||||||
|
this->showCategory(foodEmoji);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(activityEmoji, &Category::emojiSelected, this, &Panel::emojiSelected);
|
||||||
|
connect(activityCategory, &QPushButton::clicked, [this, activityEmoji]() {
|
||||||
|
this->showCategory(activityEmoji);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(travelEmoji, &Category::emojiSelected, this, &Panel::emojiSelected);
|
||||||
|
connect(travelCategory, &QPushButton::clicked, [this, travelEmoji]() {
|
||||||
|
this->showCategory(travelEmoji);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(objectsEmoji, &Category::emojiSelected, this, &Panel::emojiSelected);
|
||||||
|
connect(objectsCategory, &QPushButton::clicked, [this, objectsEmoji]() {
|
||||||
|
this->showCategory(objectsEmoji);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(symbolsEmoji, &Category::emojiSelected, this, &Panel::emojiSelected);
|
||||||
|
connect(symbolsCategory, &QPushButton::clicked, [this, symbolsEmoji]() {
|
||||||
|
this->showCategory(symbolsEmoji);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(flagsEmoji, &Category::emojiSelected, this, &Panel::emojiSelected);
|
||||||
|
connect(flagsCategory, &QPushButton::clicked, [this, flagsEmoji]() {
|
||||||
|
this->showCategory(flagsEmoji);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Panel::showCategory(const Category *category)
|
||||||
|
{
|
||||||
|
auto posToGo = category->mapToParent(QPoint()).y();
|
||||||
|
auto current = scrollArea_->verticalScrollBar()->value();
|
||||||
|
|
||||||
|
if (current == posToGo)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// HACK
|
||||||
|
// If we want to go to a previous category and position the label at the top
|
||||||
|
// the 6*50 offset won't work because not all the categories have the same
|
||||||
|
// height. To ensure the category is at the top, we move to the top and go as
|
||||||
|
// normal to the next category.
|
||||||
|
if (current > posToGo)
|
||||||
|
this->scrollArea_->ensureVisible(0, 0, 0, 0);
|
||||||
|
|
||||||
|
posToGo += 6 * 50;
|
||||||
|
this->scrollArea_->ensureVisible(0, posToGo, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Panel::paintEvent(QPaintEvent *event)
|
||||||
|
{
|
||||||
|
Q_UNUSED(event);
|
||||||
|
|
||||||
|
QStyleOption opt;
|
||||||
|
opt.init(this);
|
||||||
|
QPainter p(this);
|
||||||
|
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
||||||
|
|
||||||
|
DropShadow::draw(p,
|
||||||
|
shadowMargin_,
|
||||||
|
4.0,
|
||||||
|
QColor(120, 120, 120, 92),
|
||||||
|
QColor(255, 255, 255, 0),
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
0.6,
|
||||||
|
width(),
|
||||||
|
height());
|
||||||
|
}
|
66
src/emoji/Panel.h
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QScrollArea>
|
||||||
|
|
||||||
|
#include "Provider.h"
|
||||||
|
|
||||||
|
namespace emoji {
|
||||||
|
|
||||||
|
class Category;
|
||||||
|
|
||||||
|
class Panel : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
Panel(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void mouseLeft();
|
||||||
|
void emojiSelected(const QString &emoji);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void leaveEvent(QEvent *event) override
|
||||||
|
{
|
||||||
|
emit leaving();
|
||||||
|
QWidget::leaveEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void leaving();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void showCategory(const Category *category);
|
||||||
|
|
||||||
|
Provider emoji_provider_;
|
||||||
|
|
||||||
|
QScrollArea *scrollArea_;
|
||||||
|
|
||||||
|
int shadowMargin_;
|
||||||
|
|
||||||
|
// Panel dimensions.
|
||||||
|
int width_;
|
||||||
|
int height_;
|
||||||
|
|
||||||
|
int categoryIconSize_;
|
||||||
|
};
|
||||||
|
} // namespace emoji
|
82
src/emoji/PickButton.cpp
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "emoji/Panel.h"
|
||||||
|
#include "emoji/PickButton.h"
|
||||||
|
|
||||||
|
using namespace emoji;
|
||||||
|
|
||||||
|
// Number of milliseconds after which the panel will be hidden
|
||||||
|
// if the mouse cursor is not on top of the widget.
|
||||||
|
constexpr int HIDE_TIMEOUT = 300;
|
||||||
|
|
||||||
|
PickButton::PickButton(QWidget *parent)
|
||||||
|
: FlatButton(parent)
|
||||||
|
, panel_{nullptr}
|
||||||
|
{
|
||||||
|
connect(&hideTimer_, &QTimer::timeout, this, &PickButton::hidePanel);
|
||||||
|
connect(this, &QPushButton::clicked, this, [this]() {
|
||||||
|
if (panel_ && panel_->isVisible()) {
|
||||||
|
hidePanel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showPanel();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PickButton::hidePanel()
|
||||||
|
{
|
||||||
|
if (panel_ && !panel_->underMouse()) {
|
||||||
|
hideTimer_.stop();
|
||||||
|
panel_->hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PickButton::showPanel()
|
||||||
|
{
|
||||||
|
if (panel_.isNull()) {
|
||||||
|
panel_ = QSharedPointer<Panel>(new Panel(this));
|
||||||
|
connect(panel_.data(), &Panel::emojiSelected, this, &PickButton::emojiSelected);
|
||||||
|
connect(panel_.data(), &Panel::leaving, this, [this]() { panel_->hide(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (panel_->isVisible())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QPoint pos(rect().x(), rect().y());
|
||||||
|
pos = this->mapToGlobal(pos);
|
||||||
|
|
||||||
|
auto panel_size = panel_->sizeHint();
|
||||||
|
|
||||||
|
auto x = pos.x() - panel_size.width() + horizontal_distance_;
|
||||||
|
auto y = pos.y() - panel_size.height() - vertical_distance_;
|
||||||
|
|
||||||
|
panel_->move(x, y);
|
||||||
|
panel_->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PickButton::leaveEvent(QEvent *e)
|
||||||
|
{
|
||||||
|
hideTimer_.start(HIDE_TIMEOUT);
|
||||||
|
FlatButton::leaveEvent(e);
|
||||||
|
}
|
55
src/emoji/PickButton.h
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QEvent>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "ui/FlatButton.h"
|
||||||
|
|
||||||
|
namespace emoji {
|
||||||
|
|
||||||
|
class Panel;
|
||||||
|
|
||||||
|
class PickButton : public FlatButton
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit PickButton(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void emojiSelected(const QString &emoji);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void leaveEvent(QEvent *e) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void showPanel();
|
||||||
|
void hidePanel();
|
||||||
|
|
||||||
|
// Vertical distance from panel's bottom.
|
||||||
|
int vertical_distance_ = 10;
|
||||||
|
|
||||||
|
// Horizontal distance from panel's bottom right corner.
|
||||||
|
int horizontal_distance_ = 70;
|
||||||
|
|
||||||
|
QSharedPointer<Panel> panel_;
|
||||||
|
QTimer hideTimer_;
|
||||||
|
};
|
||||||
|
} // namespace emoji
|
10
src/main.cpp
|
@ -127,6 +127,12 @@ main(int argc, char *argv[])
|
||||||
parser.addVersionOption();
|
parser.addVersionOption();
|
||||||
parser.process(app);
|
parser.process(app);
|
||||||
|
|
||||||
|
QFontDatabase::addApplicationFont(":/fonts/fonts/OpenSans/OpenSans-Regular.ttf");
|
||||||
|
QFontDatabase::addApplicationFont(":/fonts/fonts/OpenSans/OpenSans-Italic.ttf");
|
||||||
|
QFontDatabase::addApplicationFont(":/fonts/fonts/OpenSans/OpenSans-Bold.ttf");
|
||||||
|
QFontDatabase::addApplicationFont(":/fonts/fonts/OpenSans/OpenSans-Semibold.ttf");
|
||||||
|
QFontDatabase::addApplicationFont(":/fonts/fonts/EmojiOne/emojione-android.ttf");
|
||||||
|
|
||||||
app.setWindowIcon(QIcon(":/logos/nheko.png"));
|
app.setWindowIcon(QIcon(":/logos/nheko.png"));
|
||||||
|
|
||||||
http::init();
|
http::init();
|
||||||
|
@ -147,6 +153,10 @@ main(int argc, char *argv[])
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
|
|
||||||
QFont font;
|
QFont font;
|
||||||
|
QString userFontFamily = settings.value("user/font_family", "").toString();
|
||||||
|
if (!userFontFamily.isEmpty()) {
|
||||||
|
font.setFamily(userFontFamily);
|
||||||
|
}
|
||||||
font.setPointSizeF(settings.value("user/font_size", font.pointSizeF()).toDouble());
|
font.setPointSizeF(settings.value("user/font_size", font.pointSizeF()).toDouble());
|
||||||
|
|
||||||
app.setFont(font);
|
app.setFont(font);
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#include <QContextMenuEvent>
|
#include <QContextMenuEvent>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
|
@ -192,10 +193,17 @@ TimelineItem::init()
|
||||||
emit eventRedacted(event_id_);
|
emit eventRedacted(event_id_);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
connect(
|
||||||
|
ChatPage::instance(), &ChatPage::themeChanged, this, &TimelineItem::refreshAuthorColor);
|
||||||
connect(markAsRead_, &QAction::triggered, this, &TimelineItem::sendReadReceipt);
|
connect(markAsRead_, &QAction::triggered, this, &TimelineItem::sendReadReceipt);
|
||||||
connect(viewRawMessage_, &QAction::triggered, this, &TimelineItem::openRawMessageViewer);
|
connect(viewRawMessage_, &QAction::triggered, this, &TimelineItem::openRawMessageViewer);
|
||||||
|
|
||||||
|
colorGenerating_ = new QFutureWatcher<QString>(this);
|
||||||
|
connect(colorGenerating_,
|
||||||
|
&QFutureWatcher<QString>::finished,
|
||||||
|
this,
|
||||||
|
&TimelineItem::finishedGeneratingColor);
|
||||||
|
|
||||||
topLayout_ = new QHBoxLayout(this);
|
topLayout_ = new QHBoxLayout(this);
|
||||||
mainLayout_ = new QVBoxLayout;
|
mainLayout_ = new QVBoxLayout;
|
||||||
messageLayout_ = new QHBoxLayout;
|
messageLayout_ = new QHBoxLayout;
|
||||||
|
@ -556,6 +564,12 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Text>
|
||||||
adjustMessageLayout();
|
adjustMessageLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimelineItem::~TimelineItem()
|
||||||
|
{
|
||||||
|
colorGenerating_->cancel();
|
||||||
|
colorGenerating_->waitForFinished();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineItem::markSent()
|
TimelineItem::markSent()
|
||||||
{
|
{
|
||||||
|
@ -594,7 +608,7 @@ TimelineItem::markReceived(bool isEncrypted)
|
||||||
void
|
void
|
||||||
TimelineItem::generateBody(const QString &body)
|
TimelineItem::generateBody(const QString &body)
|
||||||
{
|
{
|
||||||
body_ = new TextLabel(body, this);
|
body_ = new TextLabel(replaceEmoji(body), this);
|
||||||
body_->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction);
|
body_->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction);
|
||||||
|
|
||||||
connect(body_, &TextLabel::userProfileTriggered, this, [](const QString &user_id) {
|
connect(body_, &TextLabel::userProfileTriggered, this, [](const QString &user_id) {
|
||||||
|
@ -603,6 +617,47 @@ TimelineItem::generateBody(const QString &body)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TimelineItem::refreshAuthorColor()
|
||||||
|
{
|
||||||
|
// Cancel and wait if we are already generating the color.
|
||||||
|
if (colorGenerating_->isRunning()) {
|
||||||
|
colorGenerating_->cancel();
|
||||||
|
colorGenerating_->waitForFinished();
|
||||||
|
}
|
||||||
|
if (userName_) {
|
||||||
|
// generate user's unique color.
|
||||||
|
std::function<QString()> generate = [this]() {
|
||||||
|
QString userColor = utils::generateContrastingHexColor(
|
||||||
|
userName_->toolTip(), backgroundColor().name());
|
||||||
|
return userColor;
|
||||||
|
};
|
||||||
|
|
||||||
|
QString userColor = Cache::userColor(userName_->toolTip());
|
||||||
|
|
||||||
|
// If the color is empty, then generate it asynchronously
|
||||||
|
if (userColor.isEmpty()) {
|
||||||
|
colorGenerating_->setFuture(QtConcurrent::run(generate));
|
||||||
|
} else {
|
||||||
|
userName_->setStyleSheet("QLabel { color : " + userColor + "; }");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TimelineItem::finishedGeneratingColor()
|
||||||
|
{
|
||||||
|
nhlog::ui()->debug("finishedGeneratingColor for: {}", userName_->toolTip().toStdString());
|
||||||
|
QString userColor = colorGenerating_->result();
|
||||||
|
|
||||||
|
if (!userColor.isEmpty()) {
|
||||||
|
// another TimelineItem might have inserted in the meantime.
|
||||||
|
if (Cache::userColor(userName_->toolTip()).isEmpty()) {
|
||||||
|
Cache::insertUserColor(userName_->toolTip(), userColor);
|
||||||
|
}
|
||||||
|
userName_->setStyleSheet("QLabel { color : " + userColor + "; }");
|
||||||
|
}
|
||||||
|
}
|
||||||
// The username/timestamp is displayed along with the message body.
|
// The username/timestamp is displayed along with the message body.
|
||||||
void
|
void
|
||||||
TimelineItem::generateBody(const QString &user_id, const QString &displayname, const QString &body)
|
TimelineItem::generateBody(const QString &user_id, const QString &displayname, const QString &body)
|
||||||
|
@ -623,7 +678,7 @@ TimelineItem::generateUserName(const QString &user_id, const QString &displaynam
|
||||||
}
|
}
|
||||||
|
|
||||||
QFont usernameFont;
|
QFont usernameFont;
|
||||||
usernameFont.setPointSizeF(usernameFont.pointSizeF());
|
usernameFont.setPointSizeF(usernameFont.pointSizeF() * 1.1);
|
||||||
usernameFont.setWeight(QFont::Medium);
|
usernameFont.setWeight(QFont::Medium);
|
||||||
|
|
||||||
QFontMetrics fm(usernameFont);
|
QFontMetrics fm(usernameFont);
|
||||||
|
@ -637,6 +692,10 @@ TimelineItem::generateUserName(const QString &user_id, const QString &displaynam
|
||||||
userName_->setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
userName_->setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
||||||
userName_->setFixedWidth(QFontMetrics(userName_->font()).width(userName_->text()));
|
userName_->setFixedWidth(QFontMetrics(userName_->font()).width(userName_->text()));
|
||||||
|
|
||||||
|
// Set the user color asynchronously if it hasn't been generated yet,
|
||||||
|
// otherwise this will just set it.
|
||||||
|
refreshAuthorColor();
|
||||||
|
|
||||||
auto filter = new UserProfileFilter(user_id, userName_);
|
auto filter = new UserProfileFilter(user_id, userName_);
|
||||||
userName_->installEventFilter(filter);
|
userName_->installEventFilter(filter);
|
||||||
userName_->setCursor(Qt::PointingHandCursor);
|
userName_->setCursor(Qt::PointingHandCursor);
|
||||||
|
@ -667,6 +726,25 @@ TimelineItem::generateTimestamp(const QDateTime &time)
|
||||||
QString("<span style=\"color: #999\"> %1 </span>").arg(time.toString("HH:mm")));
|
QString("<span style=\"color: #999\"> %1 </span>").arg(time.toString("HH:mm")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
TimelineItem::replaceEmoji(const QString &body)
|
||||||
|
{
|
||||||
|
QString fmtBody = "";
|
||||||
|
|
||||||
|
QVector<uint> utf32_string = body.toUcs4();
|
||||||
|
|
||||||
|
for (auto &code : utf32_string) {
|
||||||
|
// TODO: Be more precise here.
|
||||||
|
if (code > 9000)
|
||||||
|
fmtBody += QString("<span style=\"font-family: emoji;\">") +
|
||||||
|
QString::fromUcs4(&code, 1) + "</span>";
|
||||||
|
else
|
||||||
|
fmtBody += QString::fromUcs4(&code, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmtBody;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineItem::setupAvatarLayout(const QString &userName)
|
TimelineItem::setupAvatarLayout(const QString &userName)
|
||||||
{
|
{
|
||||||
|
@ -837,4 +915,4 @@ TimelineItem::openRawMessageViewer() const
|
||||||
"failed to serialize event ({}, {})", room_id, event_id);
|
"failed to serialize event ({}, {})", room_id, event_id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
|
@ -26,6 +26,8 @@
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <QtConcurrent>
|
||||||
|
|
||||||
#include "AvatarProvider.h"
|
#include "AvatarProvider.h"
|
||||||
#include "RoomInfoListItem.h"
|
#include "RoomInfoListItem.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
@ -132,6 +134,8 @@ private:
|
||||||
class TimelineItem : public QWidget
|
class TimelineItem : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Notice> &e,
|
TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Notice> &e,
|
||||||
bool with_sender,
|
bool with_sender,
|
||||||
|
@ -202,6 +206,11 @@ public:
|
||||||
const QString &room_id,
|
const QString &room_id,
|
||||||
QWidget *parent);
|
QWidget *parent);
|
||||||
|
|
||||||
|
~TimelineItem();
|
||||||
|
|
||||||
|
void setBackgroundColor(const QColor &color) { backgroundColor_ = color; }
|
||||||
|
QColor backgroundColor() const { return backgroundColor_; }
|
||||||
|
|
||||||
void setUserAvatar(const QImage &pixmap);
|
void setUserAvatar(const QImage &pixmap);
|
||||||
DescInfo descriptionMessage() const { return descriptionMsg_; }
|
DescInfo descriptionMessage() const { return descriptionMsg_; }
|
||||||
QString eventId() const { return event_id_; }
|
QString eventId() const { return event_id_; }
|
||||||
|
@ -222,6 +231,10 @@ signals:
|
||||||
void eventRedacted(const QString &event_id);
|
void eventRedacted(const QString &event_id);
|
||||||
void redactionFailed(const QString &msg);
|
void redactionFailed(const QString &msg);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void refreshAuthorColor();
|
||||||
|
void finishedGeneratingColor();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *event) override;
|
void paintEvent(QPaintEvent *event) override;
|
||||||
void contextMenuEvent(QContextMenuEvent *event) override;
|
void contextMenuEvent(QContextMenuEvent *event) override;
|
||||||
|
@ -256,6 +269,9 @@ private:
|
||||||
//! has been acknowledged by the server.
|
//! has been acknowledged by the server.
|
||||||
bool isReceived_ = false;
|
bool isReceived_ = false;
|
||||||
|
|
||||||
|
QFutureWatcher<QString> *colorGenerating_;
|
||||||
|
|
||||||
|
QString replaceEmoji(const QString &body);
|
||||||
QString event_id_;
|
QString event_id_;
|
||||||
QString room_id_;
|
QString room_id_;
|
||||||
|
|
||||||
|
@ -282,6 +298,8 @@ private:
|
||||||
QLabel *timestamp_;
|
QLabel *timestamp_;
|
||||||
QLabel *userName_;
|
QLabel *userName_;
|
||||||
TextLabel *body_;
|
TextLabel *body_;
|
||||||
|
|
||||||
|
QColor backgroundColor_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class Widget>
|
template<class Widget>
|
||||||
|
|
|
@ -158,6 +158,7 @@ ImageItem::mousePressEvent(QMouseEvent *event)
|
||||||
} else {
|
} else {
|
||||||
auto imgDialog = new dialogs::ImageOverlay(image_);
|
auto imgDialog = new dialogs::ImageOverlay(image_);
|
||||||
imgDialog->show();
|
imgDialog->show();
|
||||||
|
connect(imgDialog, &dialogs::ImageOverlay::saving, this, &ImageItem::saveAs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|