Build custom Qt for AppImage (#19)
This commit is contained in:
parent
0b44e14f28
commit
89037d1c8f
3 changed files with 1169 additions and 44 deletions
206
.github/workflows/appimage-release.yml
vendored
206
.github/workflows/appimage-release.yml
vendored
|
|
@ -15,9 +15,13 @@ jobs:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
GIT: "https://github.com"
|
GIT: "https://github.com"
|
||||||
|
QT: "5_12_5"
|
||||||
OPENSSL_VER: "1_1_1"
|
OPENSSL_VER: "1_1_1"
|
||||||
CMAKE_VER: "3.17.0"
|
CMAKE_VER: "3.17.0"
|
||||||
|
ONLY_CACHE: "false"
|
||||||
|
MANUAL_CACHING: "1"
|
||||||
DOC_PATH: "docs/building-cmake.md"
|
DOC_PATH: "docs/building-cmake.md"
|
||||||
|
AUTO_CACHING: "1"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Get repository name.
|
- name: Get repository name.
|
||||||
|
|
@ -41,13 +45,15 @@ jobs:
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install software-properties-common -y && \
|
sudo apt-get install software-properties-common -y && \
|
||||||
sudo add-apt-repository ppa:git-core/ppa -y && \
|
sudo add-apt-repository ppa:git-core/ppa -y && \
|
||||||
sudo add-apt-repository ppa:beineri/opt-qt-5.10.1-trusty -y && \
|
|
||||||
sudo apt-get update && \
|
sudo apt-get update && \
|
||||||
sudo apt-get install liblzma-dev zlib1g-dev \
|
sudo apt-get install libice-dev libsm-dev libicu-dev liblzma-dev zlib1g-dev \
|
||||||
git wget autoconf automake build-essential libtool pkg-config \
|
git wget autoconf automake build-essential libtool pkg-config bison \
|
||||||
libopus-dev libasound2-dev libpulse-dev portaudio19-dev \
|
libopus-dev libasound2-dev libpulse-dev portaudio19-dev libdrm-dev libfuse2 \
|
||||||
qt510base qt510imageformats qt510svg libfuse2 libgl1-mesa-dev \
|
libgtk-3-dev libgtk2.0-dev libatspi2.0-dev libgl1-mesa-dev libegl1-mesa-dev \
|
||||||
libva-dev libvdpau-dev python3 python3-pip unzip -y --force-yes && \
|
libffi-dev libxcb1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-xfixes0-dev \
|
||||||
|
libxcb-keysyms1-dev libxcb-icccm4-dev libxcb-render-util0-dev libxcb-util0-dev \
|
||||||
|
libxcb-xkb-dev libxcb-sync0-dev libxcb-randr0-dev libx11-xcb-dev libxrender-dev \
|
||||||
|
xutils-dev libva-dev libvdpau-dev python3 python3-pip unzip -y --force-yes && \
|
||||||
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y && \
|
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y && \
|
||||||
sudo apt-get update && \
|
sudo apt-get update && \
|
||||||
sudo apt-get install gcc-8 g++-8 -y && \
|
sudo apt-get install gcc-8 g++-8 -y && \
|
||||||
|
|
@ -71,6 +77,15 @@ jobs:
|
||||||
./linuxdeployqt-continuous-x86_64.AppImage -version
|
./linuxdeployqt-continuous-x86_64.AppImage -version
|
||||||
gcc --version
|
gcc --version
|
||||||
|
|
||||||
|
gcc --version > CACHE_KEY.txt
|
||||||
|
echo $MANUAL_CACHING >> CACHE_KEY.txt
|
||||||
|
if [ "$AUTO_CACHING" == "1" ]; then
|
||||||
|
thisFile=$REPO_NAME/.github/workflows/linux.yml
|
||||||
|
echo `md5sum $thisFile | cut -c -32` >> CACHE_KEY.txt
|
||||||
|
fi
|
||||||
|
md5cache=$(md5sum CACHE_KEY.txt | cut -c -32)
|
||||||
|
echo ::set-env name=CACHE_KEY::$md5cache
|
||||||
|
|
||||||
mkdir -p Libraries
|
mkdir -p Libraries
|
||||||
cd Libraries
|
cd Libraries
|
||||||
echo ::set-env name=LibrariesPath::`pwd`
|
echo ::set-env name=LibrariesPath::`pwd`
|
||||||
|
|
@ -191,7 +206,14 @@ jobs:
|
||||||
# cd ../..
|
# cd ../..
|
||||||
# rm -rf dav1d
|
# rm -rf dav1d
|
||||||
|
|
||||||
- name: FFmpeg.
|
- name: FFmpeg cache.
|
||||||
|
id: cache-ffmpeg
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ${{ env.LibrariesPath }}/ffmpeg-cache
|
||||||
|
key: ${{ runner.OS }}-ffmpeg-${{ env.CACHE_KEY }}
|
||||||
|
- name: FFmpeg build.
|
||||||
|
if: steps.cache-ffmpeg.outputs.cache-hit != 'true'
|
||||||
run: |
|
run: |
|
||||||
cd $LibrariesPath
|
cd $LibrariesPath
|
||||||
|
|
||||||
|
|
@ -263,9 +285,13 @@ jobs:
|
||||||
--enable-muxer=opus
|
--enable-muxer=opus
|
||||||
|
|
||||||
make -j$(nproc)
|
make -j$(nproc)
|
||||||
sudo make install
|
sudo make DESTDIR="$LibrariesPath/ffmpeg-cache" install
|
||||||
cd ..
|
cd ..
|
||||||
rm -rf ffmpeg
|
rm -rf ffmpeg
|
||||||
|
- name: FFmpeg install.
|
||||||
|
run: |
|
||||||
|
cd $LibrariesPath
|
||||||
|
sudo cp -R ffmpeg-cache/. /
|
||||||
|
|
||||||
- name: OpenAL Soft.
|
- name: OpenAL Soft.
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -286,7 +312,14 @@ jobs:
|
||||||
cd -
|
cd -
|
||||||
rm -rf openal-soft
|
rm -rf openal-soft
|
||||||
|
|
||||||
- name: OpenSSL.
|
- name: OpenSSL cache.
|
||||||
|
id: cache-openssl
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ${{ env.LibrariesPath }}/openssl-cache
|
||||||
|
key: ${{ runner.OS }}-${{ env.OPENSSL_VER }}-${{ env.CACHE_KEY }}
|
||||||
|
- name: OpenSSL build.
|
||||||
|
if: steps.cache-openssl.outputs.cache-hit != 'true'
|
||||||
run: |
|
run: |
|
||||||
cd $LibrariesPath
|
cd $LibrariesPath
|
||||||
|
|
||||||
|
|
@ -296,9 +329,13 @@ jobs:
|
||||||
cd $opensslDir
|
cd $opensslDir
|
||||||
./config --prefix=/usr
|
./config --prefix=/usr
|
||||||
make -j$(nproc)
|
make -j$(nproc)
|
||||||
sudo make install_sw
|
sudo make DESTDIR="$LibrariesPath/openssl-cache" install_sw
|
||||||
cd ..
|
cd ..
|
||||||
rm -rf $opensslDir
|
rm -rf $opensslDir
|
||||||
|
- name: OpenSSL install.
|
||||||
|
run: |
|
||||||
|
cd $LibrariesPath
|
||||||
|
sudo cp -R openssl-cache/. /
|
||||||
|
|
||||||
- name: Hunspell.
|
- name: Hunspell.
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -313,13 +350,91 @@ jobs:
|
||||||
cd ..
|
cd ..
|
||||||
rm -rf hunspell
|
rm -rf hunspell
|
||||||
|
|
||||||
- name: libdbusmenu-qt.
|
- name: Libxkbcommon.
|
||||||
shell: bash --noprofile --norc -o pipefail {0}
|
|
||||||
run: |
|
run: |
|
||||||
cd $LibrariesPath
|
cd $LibrariesPath
|
||||||
|
|
||||||
source /opt/qt*/bin/qt*-env.sh
|
git clone -b xkbcommon-0.8.4 --depth=1 $GIT/xkbcommon/libxkbcommon.git
|
||||||
export CMAKE_PREFIX_PATH=$QTDIR
|
cd libxkbcommon
|
||||||
|
./autogen.sh
|
||||||
|
make -j$(nproc)
|
||||||
|
sudo make install
|
||||||
|
cd ..
|
||||||
|
rm -rf libxkbcommon
|
||||||
|
|
||||||
|
- name: Libwayland.
|
||||||
|
run: |
|
||||||
|
cd $LibrariesPath
|
||||||
|
|
||||||
|
git clone -b 1.16 https://gitlab.freedesktop.org/wayland/wayland
|
||||||
|
cd wayland
|
||||||
|
./autogen.sh --disable-documentation --disable-dtd-validation
|
||||||
|
make -j$(nproc)
|
||||||
|
sudo make install
|
||||||
|
cd ..
|
||||||
|
rm -rf wayland
|
||||||
|
|
||||||
|
- name: Qt 5.12.5 cache.
|
||||||
|
id: cache-qt
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ${{ env.LibrariesPath }}/qt-cache
|
||||||
|
key: ${{ runner.OS }}-qt-${{ env.CACHE_KEY }}-${{ hashFiles('**/qtbase_5_12_5.diff') }}
|
||||||
|
- name: Qt 5.12.5 build.
|
||||||
|
if: steps.cache-qt.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
cd $LibrariesPath
|
||||||
|
|
||||||
|
git clone -b v5.12.5 --depth=1 git://code.qt.io/qt/qt5.git qt_${QT}
|
||||||
|
cd qt_${QT}
|
||||||
|
perl init-repository --module-subset=qtbase,qtwayland,qtimageformats,qtsvg
|
||||||
|
git submodule update qtbase qtwayland qtimageformats qtsvg
|
||||||
|
cd qtbase
|
||||||
|
git apply ../../../$REPO_NAME/Telegram/Patches/qtbase_${QT}_appimage.diff
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
./configure -prefix /usr \
|
||||||
|
-release \
|
||||||
|
-opensource \
|
||||||
|
-confirm-license \
|
||||||
|
-qt-zlib \
|
||||||
|
-qt-libpng \
|
||||||
|
-qt-libjpeg \
|
||||||
|
-qt-harfbuzz \
|
||||||
|
-qt-pcre \
|
||||||
|
-qt-xcb \
|
||||||
|
-system-freetype \
|
||||||
|
-fontconfig \
|
||||||
|
-dbus-linked \
|
||||||
|
-openssl-linked \
|
||||||
|
-nomake examples \
|
||||||
|
-nomake tests
|
||||||
|
|
||||||
|
make -j$(nproc)
|
||||||
|
sudo make INSTALL_ROOT="$LibrariesPath/qt-cache" install
|
||||||
|
cd ..
|
||||||
|
rm -rf qt_${QT}
|
||||||
|
- name: Qt 5.12.5 install.
|
||||||
|
run: |
|
||||||
|
cd $LibrariesPath
|
||||||
|
sudo cp -R qt-cache/. /
|
||||||
|
|
||||||
|
- name: Qtstyleplugins.
|
||||||
|
run: |
|
||||||
|
cd $LibrariesPath
|
||||||
|
|
||||||
|
git clone --depth=1 git://code.qt.io/qt/qtstyleplugins.git
|
||||||
|
cd qtstyleplugins
|
||||||
|
qmake PREFIX='/usr'
|
||||||
|
make -j$(nproc)
|
||||||
|
sudo make install
|
||||||
|
cd ..
|
||||||
|
rm -rf qtstyleplugins
|
||||||
|
|
||||||
|
- name: Libdbusmenu-qt.
|
||||||
|
if: env.ONLY_CACHE == 'false'
|
||||||
|
run: |
|
||||||
|
cd $LibrariesPath
|
||||||
|
|
||||||
git clone -b 0.9.3+16.04.20160218-0ubuntu1 --depth=1 $GIT/unity8-team/libdbusmenu-qt.git
|
git clone -b 0.9.3+16.04.20160218-0ubuntu1 --depth=1 $GIT/unity8-team/libdbusmenu-qt.git
|
||||||
cd libdbusmenu-qt
|
cd libdbusmenu-qt
|
||||||
|
|
@ -333,18 +448,14 @@ jobs:
|
||||||
cd ..
|
cd ..
|
||||||
rm -rf libdbusmenu-qt
|
rm -rf libdbusmenu-qt
|
||||||
|
|
||||||
- name: Kotatogram Desktop.
|
- name: Kotatogram Desktop build.
|
||||||
shell: bash --noprofile --norc -o pipefail {0}
|
if: env.ONLY_CACHE == 'false'
|
||||||
env:
|
env:
|
||||||
API_ID: ${{ secrets.API_ID }}
|
API_ID: ${{ secrets.API_ID }}
|
||||||
API_HASH: ${{ secrets.API_HASH }}
|
API_HASH: ${{ secrets.API_HASH }}
|
||||||
run: |
|
run: |
|
||||||
cd $REPO_NAME
|
cd $REPO_NAME
|
||||||
|
|
||||||
mkdir AppDir
|
|
||||||
source /opt/qt*/bin/qt*-env.sh
|
|
||||||
export CMAKE_PREFIX_PATH=$QTDIR
|
|
||||||
|
|
||||||
cmake . \
|
cmake . \
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
|
@ -358,50 +469,57 @@ jobs:
|
||||||
-DTDESKTOP_USE_PACKAGED_TGVOIP=OFF
|
-DTDESKTOP_USE_PACKAGED_TGVOIP=OFF
|
||||||
|
|
||||||
cmake --build .
|
cmake --build .
|
||||||
|
|
||||||
|
mkdir AppDir
|
||||||
DESTDIR=AppDir cmake --install .
|
DESTDIR=AppDir cmake --install .
|
||||||
|
|
||||||
- name: Update linker cache.
|
- name: Update linker cache.
|
||||||
|
if: env.ONLY_CACHE == 'false'
|
||||||
run: sudo ldconfig
|
run: sudo ldconfig
|
||||||
|
|
||||||
- name: AppImage build.
|
- name: AppImageKit-checkrt build.
|
||||||
shell: bash --noprofile --norc -o pipefail {0}
|
if: env.ONLY_CACHE == 'false'
|
||||||
run: |
|
run: |
|
||||||
source /opt/qt*/bin/qt*-env.sh
|
cd $LibrariesPath
|
||||||
|
|
||||||
./linuxdeployqt-continuous-x86_64.AppImage \
|
git clone --depth=1 $GIT/darealshinji/AppImageKit-checkrt
|
||||||
$REPO_NAME/AppDir/usr/share/applications/*.desktop \
|
cd AppImageKit-checkrt
|
||||||
-bundle-non-qt-libs \
|
git apply ../../$REPO_NAME/Telegram/Patches/AppImageKit-checkrt.diff
|
||||||
-extra-plugins=bearer,iconengines,imageformats,platforminputcontexts,platformthemes/libqgtk3.so
|
make -j$(nproc)
|
||||||
|
|
||||||
|
- name: AppImage build.
|
||||||
|
if: env.ONLY_CACHE == 'false'
|
||||||
|
run: |
|
||||||
|
# Let appimagetool determine the repository
|
||||||
|
cd $REPO_NAME
|
||||||
|
|
||||||
# Workaround to increase compatibility with older systems; see https://github.com/darealshinji/AppImageKit-checkrt for details
|
# Workaround to increase compatibility with older systems; see https://github.com/darealshinji/AppImageKit-checkrt for details
|
||||||
mkdir -p $REPO_NAME/AppDir/usr/optional
|
install -D $LibrariesPath/AppImageKit-checkrt/exec.so AppDir/usr/optional/exec.so
|
||||||
wget -c https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/exec-x86_64.so -O $REPO_NAME/AppDir/usr/optional/exec.so
|
install -D $LibrariesPath/AppImageKit-checkrt/AppRun_patched AppDir/AppRun
|
||||||
|
install -D /usr/lib/x86_64-linux-gnu/libstdc++.so.6 AppDir/usr/optional/libstdc++/libstdc++.so.6
|
||||||
|
|
||||||
mkdir -p $REPO_NAME/AppDir/usr/optional/libstdc++
|
../linuxdeployqt-continuous-x86_64.AppImage \
|
||||||
cp /usr/lib/x86_64-linux-gnu/libstdc++.so.6 $REPO_NAME/AppDir/usr/optional/libstdc++
|
AppDir/usr/share/applications/*.desktop \
|
||||||
|
-appimage \
|
||||||
|
-exclude-libs=libatk-1.0.so.0,libatk-bridge-2.0.so.0,libatspi.so.0,libcairo-gobject.so.2,libcairo.so.2,libgdk-3.so.0,libgdk-x11-2.0.so.0,libgmodule-2.0.so.0,libgtk-3.so.0,libgtk-x11-2.0.so.0,libpixman-1.so.0,libpng12.so.0 \
|
||||||
|
-extra-plugins=bearer,iconengines,imageformats,platforminputcontexts,platforms/libqwayland-egl.so,platforms/libqwayland-generic.so,platformthemes/libqgtk3.so,wayland-decoration-client,wayland-graphics-integration-client,wayland-shell-integration
|
||||||
|
|
||||||
pushd $REPO_NAME/AppDir
|
- name: Get artifact name.
|
||||||
rm AppRun
|
if: env.ONLY_CACHE == 'false'
|
||||||
wget -c https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/AppRun-patched-x86_64 -O AppRun
|
|
||||||
chmod a+x AppRun
|
|
||||||
popd
|
|
||||||
|
|
||||||
./linuxdeployqt-continuous-x86_64.AppImage \
|
|
||||||
$REPO_NAME/AppDir/usr/share/applications/*.desktop \
|
|
||||||
-appimage
|
|
||||||
|
|
||||||
- name: Get artifact name
|
|
||||||
run: |
|
run: |
|
||||||
|
cd $REPO_NAME
|
||||||
|
|
||||||
artifact_name=$(echo Kotatogram_Desktop*.AppImage)
|
artifact_name=$(echo Kotatogram_Desktop*.AppImage)
|
||||||
echo ::set-env name=ARTIFACT_NAME::$artifact_name
|
echo ::set-env name=ARTIFACT_NAME::$artifact_name
|
||||||
|
|
||||||
- name: Upload Release Asset
|
- name: Upload release asset.
|
||||||
id: upload-release-asset
|
id: upload-release-asset
|
||||||
|
if: env.ONLY_CACHE == 'false'
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ github.event.release.upload_url }}
|
upload_url: ${{ github.event.release.upload_url }}
|
||||||
asset_path: ./${{ env.ARTIFACT_NAME }}
|
asset_path: ./${{ env.REPO_NAME }}/${{ env.ARTIFACT_NAME }}
|
||||||
asset_name: ${{ env.ARTIFACT_NAME }}
|
asset_name: ${{ env.ARTIFACT_NAME }}
|
||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
|
|
|
||||||
25
Telegram/Patches/AppImageKit-checkrt.diff
Normal file
25
Telegram/Patches/AppImageKit-checkrt.diff
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
diff --git a/Makefile b/Makefile
|
||||||
|
index cd8fe34..dab0cff 100644
|
||||||
|
--- a/Makefile
|
||||||
|
+++ b/Makefile
|
||||||
|
@@ -38,6 +38,6 @@ AppRun_patched.c: AppRun.c
|
||||||
|
patch -p1 --output $@ < AppRun.c.patch
|
||||||
|
|
||||||
|
AppRun.c:
|
||||||
|
- wget -c "https://raw.githubusercontent.com/AppImage/AppImageKit/appimagetool/master/src/AppRun.c"
|
||||||
|
+ wget -c "https://raw.githubusercontent.com/AppImage/AppImageKit/master/src/AppRun.c"
|
||||||
|
|
||||||
|
.PHONY: checkrt test run_tests all clean
|
||||||
|
diff --git a/checkrt.c b/checkrt.c
|
||||||
|
index 7607d61..f5a187f 100644
|
||||||
|
--- a/checkrt.c
|
||||||
|
+++ b/checkrt.c
|
||||||
|
@@ -72,7 +72,7 @@ void checkrt(char *usr_in_appdir)
|
||||||
|
SCANLIB(stdcxx_sys_lib, stdcxx_sys_sym, "^GLIBCXX_3\\.4");
|
||||||
|
SCANLIB(stdcxx_bundle_lib, stdcxx_bundle_sym, "^GLIBCXX_3\\.4");
|
||||||
|
stdcxx_sys_ver = atoi(stdcxx_sys_sym+12);
|
||||||
|
- stdcxx_bundle_ver = atoi(stdcxx_bundle_sym+12);
|
||||||
|
+ stdcxx_bundle_ver = 21;
|
||||||
|
DEBUG("%s ==> %s (%d)\n", stdcxx_sys_lib, stdcxx_sys_sym, stdcxx_sys_ver);
|
||||||
|
DEBUG("%s ==> %s (%d)\n\n", stdcxx_bundle_lib, stdcxx_bundle_sym, stdcxx_bundle_ver);
|
||||||
|
}
|
||||||
982
Telegram/Patches/qtbase_5_12_5_appimage.diff
Normal file
982
Telegram/Patches/qtbase_5_12_5_appimage.diff
Normal file
|
|
@ -0,0 +1,982 @@
|
||||||
|
diff --git a/src/corelib/kernel/qcore_mac_objc.mm b/src/corelib/kernel/qcore_mac_objc.mm
|
||||||
|
index 266faca0ed..cf9dafb6d8 100644
|
||||||
|
--- a/src/corelib/kernel/qcore_mac_objc.mm
|
||||||
|
+++ b/src/corelib/kernel/qcore_mac_objc.mm
|
||||||
|
@@ -140,7 +140,8 @@ QMacAutoReleasePool::QMacAutoReleasePool()
|
||||||
|
{
|
||||||
|
Class trackerClass = [QMacAutoReleasePoolTracker class];
|
||||||
|
|
||||||
|
-#ifdef QT_DEBUG
|
||||||
|
+// Patch: Disable this debug code because it is very slow.
|
||||||
|
+#ifdef QT_DEBUG____REMOVED
|
||||||
|
void *poolFrame = nullptr;
|
||||||
|
if (__builtin_available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 5.0, *)) {
|
||||||
|
void *frame;
|
||||||
|
diff --git a/src/gui/kernel/qstylehints.cpp b/src/gui/kernel/qstylehints.cpp
|
||||||
|
index 48060a2c37..fff3271ec9 100644
|
||||||
|
--- a/src/gui/kernel/qstylehints.cpp
|
||||||
|
+++ b/src/gui/kernel/qstylehints.cpp
|
||||||
|
@@ -374,7 +374,11 @@ bool QStyleHints::showIsMaximized() const
|
||||||
|
*/
|
||||||
|
bool QStyleHints::showShortcutsInContextMenus() const
|
||||||
|
{
|
||||||
|
- return themeableHint(QPlatformTheme::ShowShortcutsInContextMenus, QPlatformIntegration::ShowShortcutsInContextMenus).toBool();
|
||||||
|
+ // Patch: Always show hotkeys in the standard context menu.
|
||||||
|
+ // This patch can be removed in 5.13 and later versions.
|
||||||
|
+ // See: https://bugreports.qt.io/browse/QTBUG-71471
|
||||||
|
+ return true;
|
||||||
|
+ // return themeableHint(QPlatformTheme::ShowShortcutsInContextMenus, QPlatformIntegration::ShowShortcutsInContextMenus).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
diff --git a/src/gui/painting/qbezier.cpp b/src/gui/painting/qbezier.cpp
|
||||||
|
index 65e6063fe4..fcf19a1a63 100644
|
||||||
|
--- a/src/gui/painting/qbezier.cpp
|
||||||
|
+++ b/src/gui/painting/qbezier.cpp
|
||||||
|
@@ -400,6 +400,33 @@ static bool addCircle(const QBezier *b, qreal offset, QBezier *o)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
+// Patch: Workaround VS2019 compiler bug, see QTBUG-75280.
|
||||||
|
+#ifdef Q_OS_WIN
|
||||||
|
+Q_NEVER_INLINE void QBezier::split(QBezier *firstHalf, QBezier *secondHalf) const
|
||||||
|
+{
|
||||||
|
+ Q_ASSERT(firstHalf);
|
||||||
|
+ Q_ASSERT(secondHalf);
|
||||||
|
+
|
||||||
|
+ qreal c = (x2 + x3)*.5;
|
||||||
|
+ firstHalf->x2 = (x1 + x2)*.5;
|
||||||
|
+ secondHalf->x3 = (x3 + x4)*.5;
|
||||||
|
+ firstHalf->x1 = x1;
|
||||||
|
+ secondHalf->x4 = x4;
|
||||||
|
+ firstHalf->x3 = (firstHalf->x2 + c)*.5;
|
||||||
|
+ secondHalf->x2 = (secondHalf->x3 + c)*.5;
|
||||||
|
+ firstHalf->x4 = secondHalf->x1 = (firstHalf->x3 + secondHalf->x2)*.5;
|
||||||
|
+
|
||||||
|
+ c = (y2 + y3)/2;
|
||||||
|
+ firstHalf->y2 = (y1 + y2)*.5;
|
||||||
|
+ secondHalf->y3 = (y3 + y4)*.5;
|
||||||
|
+ firstHalf->y1 = y1;
|
||||||
|
+ secondHalf->y4 = y4;
|
||||||
|
+ firstHalf->y3 = (firstHalf->y2 + c)*.5;
|
||||||
|
+ secondHalf->y2 = (secondHalf->y3 + c)*.5;
|
||||||
|
+ firstHalf->y4 = secondHalf->y1 = (firstHalf->y3 + secondHalf->y2)*.5;
|
||||||
|
+}
|
||||||
|
+#endif // Q_OS_WIN
|
||||||
|
+
|
||||||
|
int QBezier::shifted(QBezier *curveSegments, int maxSegments, qreal offset, float threshold) const
|
||||||
|
{
|
||||||
|
Q_ASSERT(curveSegments);
|
||||||
|
diff --git a/src/gui/painting/qbezier_p.h b/src/gui/painting/qbezier_p.h
|
||||||
|
index f8a91e9ef3..50c60b2d71 100644
|
||||||
|
--- a/src/gui/painting/qbezier_p.h
|
||||||
|
+++ b/src/gui/painting/qbezier_p.h
|
||||||
|
@@ -222,6 +222,8 @@ inline QPointF QBezier::secondDerivedAt(qreal t) const
|
||||||
|
a * y1 + b * y2 + c * y3 + d * y4);
|
||||||
|
}
|
||||||
|
|
||||||
|
+// Patch: Workaround VS2019 compiler bug, see QTBUG-75280.
|
||||||
|
+#ifndef Q_OS_WIN
|
||||||
|
inline void QBezier::split(QBezier *firstHalf, QBezier *secondHalf) const
|
||||||
|
{
|
||||||
|
Q_ASSERT(firstHalf);
|
||||||
|
@@ -245,6 +247,7 @@ inline void QBezier::split(QBezier *firstHalf, QBezier *secondHalf) const
|
||||||
|
secondHalf->y2 = (secondHalf->y3 + c)*.5;
|
||||||
|
firstHalf->y4 = secondHalf->y1 = (firstHalf->y3 + secondHalf->y2)*.5;
|
||||||
|
}
|
||||||
|
+#endif // Q_OS_WIN
|
||||||
|
|
||||||
|
inline void QBezier::parameterSplitLeft(qreal t, QBezier *left)
|
||||||
|
{
|
||||||
|
diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp
|
||||||
|
index b70b29e54e..9519894076 100644
|
||||||
|
--- a/src/gui/painting/qpainter.cpp
|
||||||
|
+++ b/src/gui/painting/qpainter.cpp
|
||||||
|
@@ -6245,6 +6245,91 @@ static QPixmap generateWavyPixmap(qreal maxRadius, const QPen &pen)
|
||||||
|
return pixmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
+// Patch: Improved underline with SpellCheck style for macOS and Windows.
|
||||||
|
+// Added implementation of underline drawing from Chrome.
|
||||||
|
+static QPixmap generateChromeSpellcheckPixmap(qreal descent, qreal factor, const QPen &pen) {
|
||||||
|
+ QString key = QLatin1String("ChromeUnderline-")
|
||||||
|
+ % pen.color().name()
|
||||||
|
+ % HexString<qreal>(factor)
|
||||||
|
+ % HexString<qreal>(pen.widthF());
|
||||||
|
+
|
||||||
|
+ QPixmap pixmap;
|
||||||
|
+ if (QPixmapCache::find(key, pixmap)) {
|
||||||
|
+ return pixmap;
|
||||||
|
+ }
|
||||||
|
+ // https://chromium.googlesource.com/chromium/src/+/refs/heads/master/third_party/blink/renderer/core/paint/document_marker_painter.cc
|
||||||
|
+
|
||||||
|
+#ifdef Q_OS_MAC
|
||||||
|
+
|
||||||
|
+ constexpr qreal kMarkerHeight = 3;
|
||||||
|
+
|
||||||
|
+ const qreal height = kMarkerHeight * factor;
|
||||||
|
+ const qreal width = height * 2;
|
||||||
|
+
|
||||||
|
+ pixmap = QPixmap(qCeil(width), qFloor(height) * 2);
|
||||||
|
+ pixmap.setDevicePixelRatio(qApp->devicePixelRatio());
|
||||||
|
+ pixmap.fill(Qt::transparent);
|
||||||
|
+ {
|
||||||
|
+ QPainter imgPainter(&pixmap);
|
||||||
|
+ imgPainter.setPen(Qt::NoPen);
|
||||||
|
+ imgPainter.setBrush(pen.color());
|
||||||
|
+ imgPainter.setRenderHints(
|
||||||
|
+ QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
|
||||||
|
+ imgPainter.drawEllipse(0, 0, qFloor(height), qFloor(height));
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+#else
|
||||||
|
+
|
||||||
|
+ constexpr qreal kMarkerWidth = 4;
|
||||||
|
+ constexpr qreal kMarkerHeight = 2;
|
||||||
|
+
|
||||||
|
+ const auto x1 = (kMarkerWidth * -3 / 8) * factor;
|
||||||
|
+ const auto y1 = (kMarkerHeight * 3 / 4) * factor;
|
||||||
|
+
|
||||||
|
+ const auto cY = (kMarkerHeight * 1 / 4) * factor;
|
||||||
|
+
|
||||||
|
+ const auto c1X1 = (kMarkerWidth * -1 / 8) * factor;
|
||||||
|
+ const auto c1X2 = (kMarkerWidth * 3 / 8) * factor;
|
||||||
|
+ const auto c1X3 = (kMarkerWidth * 7 / 8) * factor;
|
||||||
|
+
|
||||||
|
+ const auto c2X1 = (kMarkerWidth * 1 / 8) * factor;
|
||||||
|
+ const auto c2X2 = (kMarkerWidth * 5 / 8) * factor;
|
||||||
|
+ const auto c2X3 = (kMarkerWidth * 9 / 8) * factor;
|
||||||
|
+
|
||||||
|
+ QPainterPath path;
|
||||||
|
+ path.moveTo(x1, y1);
|
||||||
|
+ path.cubicTo(c1X1, y1,
|
||||||
|
+ c1X1, cY,
|
||||||
|
+ c2X1, cY);
|
||||||
|
+ path.cubicTo(c1X2, cY,
|
||||||
|
+ c1X2, y1,
|
||||||
|
+ c2X2, y1);
|
||||||
|
+ path.cubicTo(c1X3, y1,
|
||||||
|
+ c1X3, cY,
|
||||||
|
+ c2X3, cY);
|
||||||
|
+
|
||||||
|
+ pixmap = QPixmap(kMarkerWidth * factor, kMarkerHeight * factor * 2);
|
||||||
|
+ pixmap.fill(Qt::transparent);
|
||||||
|
+ {
|
||||||
|
+ QPen wavePen = pen;
|
||||||
|
+ wavePen.setCapStyle(Qt::RoundCap);
|
||||||
|
+ wavePen.setJoinStyle(Qt::RoundJoin);
|
||||||
|
+ wavePen.setWidthF(1 * factor);
|
||||||
|
+
|
||||||
|
+ QPainter imgPainter(&pixmap);
|
||||||
|
+ imgPainter.setPen(std::move(wavePen));
|
||||||
|
+ imgPainter.setRenderHint(QPainter::Antialiasing);
|
||||||
|
+ imgPainter.translate(0, descent - (kMarkerHeight * factor));
|
||||||
|
+ imgPainter.drawPath(std::move(path));
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ QPixmapCache::insert(std::move(key), pixmap);
|
||||||
|
+
|
||||||
|
+ return pixmap;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const QFontEngine *fe, QTextEngine *textEngine,
|
||||||
|
QTextCharFormat::UnderlineStyle underlineStyle,
|
||||||
|
QTextItem::RenderFlags flags, qreal width,
|
||||||
|
@@ -6262,7 +6347,9 @@ static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const
|
||||||
|
pen.setWidthF(fe->lineThickness().toReal());
|
||||||
|
pen.setCapStyle(Qt::FlatCap);
|
||||||
|
|
||||||
|
- QLineF line(qFloor(pos.x()), pos.y(), qFloor(pos.x() + width), pos.y());
|
||||||
|
+ // Patch: Improved underline with SpellCheck style for macOS and Windows.
|
||||||
|
+ // Slightly move the beginning of the underline to the right.
|
||||||
|
+ QLineF line(qFloor(pos.x() + 1), pos.y(), qFloor(pos.x() + width), pos.y());
|
||||||
|
|
||||||
|
bool wasCompatiblePainting = painter->renderHints()
|
||||||
|
& QPainter::Qt4CompatiblePainting;
|
||||||
|
@@ -6273,13 +6360,29 @@ static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const
|
||||||
|
const qreal underlineOffset = fe->underlinePosition().toReal();
|
||||||
|
|
||||||
|
if (underlineStyle == QTextCharFormat::SpellCheckUnderline) {
|
||||||
|
- QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme();
|
||||||
|
- if (theme)
|
||||||
|
- underlineStyle = QTextCharFormat::UnderlineStyle(theme->themeHint(QPlatformTheme::SpellCheckUnderlineStyle).toInt());
|
||||||
|
- if (underlineStyle == QTextCharFormat::SpellCheckUnderline) // still not resolved
|
||||||
|
- underlineStyle = QTextCharFormat::WaveUnderline;
|
||||||
|
- }
|
||||||
|
+ const qreal fontFactor = qreal(charFormat.font().pixelSize()) / qreal(10.);
|
||||||
|
+ painter->save();
|
||||||
|
+ painter->translate(0, pos.y() + 1);
|
||||||
|
+ const qreal maxHeight = fe->descent().toReal() - qreal(1);
|
||||||
|
+
|
||||||
|
+ QColor uc = charFormat.underlineColor();
|
||||||
|
+ if (uc.isValid())
|
||||||
|
+ pen.setColor(uc);
|
||||||
|
|
||||||
|
+ const QPixmap wave = generateChromeSpellcheckPixmap(maxHeight, fontFactor, pen);
|
||||||
|
+ const int descent = qFloor(maxHeight);
|
||||||
|
+
|
||||||
|
+ painter->setBrushOrigin(painter->brushOrigin().x(), 0);
|
||||||
|
+#ifdef Q_OS_MAC
|
||||||
|
+ const auto h = wave.height() / 2;
|
||||||
|
+ painter->drawTiledPixmap(
|
||||||
|
+ QRectF(pos.x(), (descent - h) / 2., qCeil(width), h),
|
||||||
|
+ wave);
|
||||||
|
+#else
|
||||||
|
+ painter->fillRect(pos.x(), 0, qCeil(width), descent, wave);
|
||||||
|
+#endif
|
||||||
|
+ painter->restore();
|
||||||
|
+ } else
|
||||||
|
if (underlineStyle == QTextCharFormat::WaveUnderline) {
|
||||||
|
painter->save();
|
||||||
|
painter->translate(0, pos.y() + 1);
|
||||||
|
diff --git a/src/gui/text/qinputcontrol.cpp b/src/gui/text/qinputcontrol.cpp
|
||||||
|
index 3381fdb673..6036f052e9 100644
|
||||||
|
--- a/src/gui/text/qinputcontrol.cpp
|
||||||
|
+++ b/src/gui/text/qinputcontrol.cpp
|
||||||
|
@@ -40,6 +40,10 @@
|
||||||
|
#include "qinputcontrol_p.h"
|
||||||
|
#include <QtGui/qevent.h>
|
||||||
|
|
||||||
|
+// Patch: Enable Ctrl+key and Ctrl+Shift+key in all locales except German.
|
||||||
|
+// See https://github.com/telegramdesktop/tdesktop/pull/1185.
|
||||||
|
+#include <QtCore/QLocale>
|
||||||
|
+
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
QInputControl::QInputControl(Type type, QObject *parent)
|
||||||
|
@@ -67,9 +71,16 @@ bool QInputControl::isAcceptableInput(const QKeyEvent *event) const
|
||||||
|
if (c.category() == QChar::Other_Format)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
- // QTBUG-35734: ignore Ctrl/Ctrl+Shift; accept only AltGr (Alt+Ctrl) on German keyboards
|
||||||
|
- if (event->modifiers() == Qt::ControlModifier
|
||||||
|
- || event->modifiers() == (Qt::ShiftModifier | Qt::ControlModifier)) {
|
||||||
|
+ // Patch: Enable Ctrl+key and Ctrl+Shift+key in all locales except German.
|
||||||
|
+ // See https://github.com/telegramdesktop/tdesktop/pull/1185.
|
||||||
|
+ bool skipCtrlAndCtrlShift = false;
|
||||||
|
+ if (QGuiApplication::inputMethod()->locale().language() == QLocale::German) {
|
||||||
|
+ if (event->modifiers() == Qt::ControlModifier
|
||||||
|
+ || event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) {
|
||||||
|
+ skipCtrlAndCtrlShift = true;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ if (skipCtrlAndCtrlShift) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/src/gui/text/qtextcursor.cpp b/src/gui/text/qtextcursor.cpp
|
||||||
|
index c88497840f..2b08d13834 100644
|
||||||
|
--- a/src/gui/text/qtextcursor.cpp
|
||||||
|
+++ b/src/gui/text/qtextcursor.cpp
|
||||||
|
@@ -510,14 +510,16 @@ bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor
|
||||||
|
const int len = blockIt.length() - 1;
|
||||||
|
if (relativePos >= len)
|
||||||
|
return false;
|
||||||
|
- if (engine->atWordSeparator(relativePos)) {
|
||||||
|
- ++relativePos;
|
||||||
|
- while (relativePos < len && engine->atWordSeparator(relativePos))
|
||||||
|
- ++relativePos;
|
||||||
|
- } else {
|
||||||
|
- while (relativePos < len && !attributes[relativePos].whiteSpace && !engine->atWordSeparator(relativePos))
|
||||||
|
- ++relativePos;
|
||||||
|
- }
|
||||||
|
+ // Patch: Improved apostrophe processing.
|
||||||
|
+ relativePos = engine->toEdge(relativePos, len, true);
|
||||||
|
+ // if (engine->atWordSeparator(relativePos)) {
|
||||||
|
+ // ++relativePos;
|
||||||
|
+ // while (relativePos < len && engine->atWordSeparator(relativePos))
|
||||||
|
+ // ++relativePos;
|
||||||
|
+ // } else {
|
||||||
|
+ // while (relativePos < len && !attributes[relativePos].whiteSpace && !engine->atWordSeparator(relativePos))
|
||||||
|
+ // ++relativePos;
|
||||||
|
+ // }
|
||||||
|
newPosition = blockIt.position() + relativePos;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp
|
||||||
|
index a7834587b1..cabe897268 100644
|
||||||
|
--- a/src/gui/text/qtextengine.cpp
|
||||||
|
+++ b/src/gui/text/qtextengine.cpp
|
||||||
|
@@ -3028,7 +3028,8 @@ bool QTextEngine::atWordSeparator(int position) const
|
||||||
|
case '&':
|
||||||
|
case '^':
|
||||||
|
case '*':
|
||||||
|
- case '\'':
|
||||||
|
+ // Patch: Make the apostrophe a non-separator for words.
|
||||||
|
+ //case '\'':
|
||||||
|
case '"':
|
||||||
|
case '`':
|
||||||
|
case '~':
|
||||||
|
@@ -3041,6 +3042,74 @@ bool QTextEngine::atWordSeparator(int position) const
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
+// Patch: Improved apostrophe processing.
|
||||||
|
+// We should consider apostrophes as word separators when there is more than
|
||||||
|
+// one apostrophe in a row, or when the apostrophe is at the beginning or end
|
||||||
|
+// of the word.
|
||||||
|
+int QTextEngine::toEdge(int pos, int len, bool isRightDirection) {
|
||||||
|
+ const auto step = isRightDirection ? 1 : -1;
|
||||||
|
+ const auto next = isRightDirection ? 0 : -1;
|
||||||
|
+
|
||||||
|
+ QCharAttributes *attributes = const_cast<QCharAttributes *>(this->attributes());
|
||||||
|
+
|
||||||
|
+ const auto atApostrophe = [&](int position) {
|
||||||
|
+ return layoutData->string.at(position).unicode() == '\'';
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ const auto atSepOrApost = [&](int position) {
|
||||||
|
+ return atApostrophe(position) || atWordSeparator(position);
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ const auto inBounds = [&](int position) {
|
||||||
|
+ return isRightDirection
|
||||||
|
+ ? position < len
|
||||||
|
+ : position > 0;
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ const auto atSepOrSpace = [&](int position) {
|
||||||
|
+ return attributes[position].whiteSpace || atWordSeparator(position);
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ const auto isApostropheInWord = [&](int position) {
|
||||||
|
+ if (!atApostrophe(position)) {
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+ auto p = position - 1;
|
||||||
|
+ if (p <= 0 || atSepOrSpace(p)) {
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+ p = position + 1;
|
||||||
|
+ if (p >= len || atSepOrSpace(p)) {
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+ return true;
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ auto counter = 0;
|
||||||
|
+ while (inBounds(pos) && atSepOrApost(pos + next)) {
|
||||||
|
+ counter++;
|
||||||
|
+ pos += step;
|
||||||
|
+ }
|
||||||
|
+ // If it's not the single apostrophe, then that's non-letter part of text.
|
||||||
|
+ if (counter > 1 || (counter == 1 && !isApostropheInWord(pos - step + next))) {
|
||||||
|
+ return pos;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ bool isPrevApostrophe = false;
|
||||||
|
+ while (inBounds(pos) && !atSepOrSpace(pos + next)) {
|
||||||
|
+ bool isNextApostrophe = atApostrophe(pos + next);
|
||||||
|
+ if (isPrevApostrophe && isNextApostrophe) {
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ pos += step;
|
||||||
|
+ isPrevApostrophe = isNextApostrophe;
|
||||||
|
+ }
|
||||||
|
+ if (isPrevApostrophe) {
|
||||||
|
+ pos += -step;
|
||||||
|
+ }
|
||||||
|
+ return pos;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
void QTextEngine::setPreeditArea(int position, const QString &preeditText)
|
||||||
|
{
|
||||||
|
if (preeditText.isEmpty()) {
|
||||||
|
diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h
|
||||||
|
index e9187ea605..51997ba066 100644
|
||||||
|
--- a/src/gui/text/qtextengine_p.h
|
||||||
|
+++ b/src/gui/text/qtextengine_p.h
|
||||||
|
@@ -622,6 +622,8 @@ private:
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool atWordSeparator(int position) const;
|
||||||
|
+ // Patch: Improved apostrophe processing.
|
||||||
|
+ int toEdge(int pos, int len, bool isRightDirection);
|
||||||
|
|
||||||
|
QString elidedText(Qt::TextElideMode mode, const QFixed &width, int flags = 0, int from = 0, int count = -1) const;
|
||||||
|
|
||||||
|
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp
|
||||||
|
index f3f0caa379..081c5f03c0 100644
|
||||||
|
--- a/src/gui/text/qtextlayout.cpp
|
||||||
|
+++ b/src/gui/text/qtextlayout.cpp
|
||||||
|
@@ -706,16 +706,22 @@ int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
|
||||||
|
while (oldPos < len && !attributes[oldPos].graphemeBoundary)
|
||||||
|
oldPos++;
|
||||||
|
} else {
|
||||||
|
- if (oldPos < len && d->atWordSeparator(oldPos)) {
|
||||||
|
- oldPos++;
|
||||||
|
- while (oldPos < len && d->atWordSeparator(oldPos))
|
||||||
|
- oldPos++;
|
||||||
|
- } else {
|
||||||
|
- while (oldPos < len && !attributes[oldPos].whiteSpace && !d->atWordSeparator(oldPos))
|
||||||
|
- oldPos++;
|
||||||
|
- }
|
||||||
|
+ // Patch: Skip to the end of the current word, not to the start of the next one.
|
||||||
|
while (oldPos < len && attributes[oldPos].whiteSpace)
|
||||||
|
oldPos++;
|
||||||
|
+ // Patch: Improved apostrophe processing.
|
||||||
|
+ oldPos = d->toEdge(oldPos, len, true);
|
||||||
|
+ // if (oldPos < len && d->atWordSeparator(oldPos)) {
|
||||||
|
+ // oldPos++;
|
||||||
|
+ // while (oldPos < len && d->atWordSeparator(oldPos))
|
||||||
|
+ // oldPos++;
|
||||||
|
+ // } else {
|
||||||
|
+ // while (oldPos < len && !attributes[oldPos].whiteSpace && !d->atWordSeparator(oldPos))
|
||||||
|
+ // oldPos++;
|
||||||
|
+ // }
|
||||||
|
+ // Patch: Skip to the end of the current word, not to the start of the next one.
|
||||||
|
+ //while (oldPos < len && attributes[oldPos].whiteSpace)
|
||||||
|
+ // oldPos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldPos;
|
||||||
|
@@ -745,14 +751,16 @@ int QTextLayout::previousCursorPosition(int oldPos, CursorMode mode) const
|
||||||
|
while (oldPos > 0 && attributes[oldPos - 1].whiteSpace)
|
||||||
|
oldPos--;
|
||||||
|
|
||||||
|
- if (oldPos && d->atWordSeparator(oldPos-1)) {
|
||||||
|
- oldPos--;
|
||||||
|
- while (oldPos && d->atWordSeparator(oldPos-1))
|
||||||
|
- oldPos--;
|
||||||
|
- } else {
|
||||||
|
- while (oldPos > 0 && !attributes[oldPos - 1].whiteSpace && !d->atWordSeparator(oldPos-1))
|
||||||
|
- oldPos--;
|
||||||
|
- }
|
||||||
|
+ // Patch: Improved apostrophe processing.
|
||||||
|
+ oldPos = d->toEdge(oldPos, len, false);
|
||||||
|
+ // if (oldPos && d->atWordSeparator(oldPos-1)) {
|
||||||
|
+ // oldPos--;
|
||||||
|
+ // while (oldPos && d->atWordSeparator(oldPos-1))
|
||||||
|
+ // oldPos--;
|
||||||
|
+ // } else {
|
||||||
|
+ // while (oldPos > 0 && !attributes[oldPos - 1].whiteSpace && !d->atWordSeparator(oldPos-1))
|
||||||
|
+ // oldPos--;
|
||||||
|
+ // }
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldPos;
|
||||||
|
diff --git a/src/platformsupport/linuxaccessibility/constant_mappings.cpp b/src/platformsupport/linuxaccessibility/constant_mappings.cpp
|
||||||
|
index fce2919e73..4a7d0f7d92 100644
|
||||||
|
--- a/src/platformsupport/linuxaccessibility/constant_mappings.cpp
|
||||||
|
+++ b/src/platformsupport/linuxaccessibility/constant_mappings.cpp
|
||||||
|
@@ -79,7 +79,12 @@ quint64 spiStatesFromQState(QAccessible::State state)
|
||||||
|
if (state.checkStateMixed)
|
||||||
|
setSpiStateBit(&spiState, ATSPI_STATE_INDETERMINATE);
|
||||||
|
if (state.readOnly)
|
||||||
|
+// Patch: Support build with AT-SPI version below 2.16.
|
||||||
|
+#ifdef ATSPI_STATE_READ_ONLY
|
||||||
|
setSpiStateBit(&spiState, ATSPI_STATE_READ_ONLY);
|
||||||
|
+#else // ATSPI_STATE_READ_ONLY
|
||||||
|
+ unsetSpiStateBit(&spiState, ATSPI_STATE_EDITABLE);
|
||||||
|
+#endif // ATSPI_STATE_READ_ONLY
|
||||||
|
// if (state.HotTracked)
|
||||||
|
if (state.defaultButton)
|
||||||
|
setSpiStateBit(&spiState, ATSPI_STATE_IS_DEFAULT);
|
||||||
|
diff --git a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp
|
||||||
|
index 81a730232c..42bab9aa4b 100644
|
||||||
|
--- a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp
|
||||||
|
+++ b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp
|
||||||
|
@@ -273,6 +273,12 @@ bool QComposeInputContext::checkComposeTable()
|
||||||
|
|
||||||
|
void QComposeInputContext::commitText(uint character) const
|
||||||
|
{
|
||||||
|
+ // Patch: Crash fix when not focused widget still receives input events.
|
||||||
|
+ if (!m_focusObject) {
|
||||||
|
+ qWarning("QComposeInputContext::commitText: m_focusObject == nullptr, cannot commit text");
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
QInputMethodEvent event;
|
||||||
|
event.setCommitString(QChar(character));
|
||||||
|
QCoreApplication::sendEvent(m_focusObject, &event);
|
||||||
|
diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
|
||||||
|
index 2cf6672da9..ef25bb4541 100644
|
||||||
|
--- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
|
||||||
|
+++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
|
||||||
|
@@ -175,7 +175,8 @@ QT_USE_NAMESPACE
|
||||||
|
if (reflectionDelegate) {
|
||||||
|
if ([reflectionDelegate respondsToSelector:@selector(applicationShouldTerminate:)])
|
||||||
|
return [reflectionDelegate applicationShouldTerminate:sender];
|
||||||
|
- return NSTerminateNow;
|
||||||
|
+ // Patch: Don't terminate if reflectionDelegate does not respond to that selector, just use the default.
|
||||||
|
+ //return NSTerminateNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([self canQuit]) {
|
||||||
|
@@ -252,7 +253,11 @@ QT_USE_NAMESPACE
|
||||||
|
|
||||||
|
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
||||||
|
{
|
||||||
|
- Q_UNUSED(aNotification);
|
||||||
|
+ // Patch: We need to catch that notification in delegate.
|
||||||
|
+ if (reflectionDelegate
|
||||||
|
+ && [reflectionDelegate respondsToSelector:@selector(applicationDidFinishLaunching:)])
|
||||||
|
+ [reflectionDelegate applicationDidFinishLaunching:aNotification];
|
||||||
|
+
|
||||||
|
inLaunch = false;
|
||||||
|
|
||||||
|
if (qEnvironmentVariableIsEmpty("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM")) {
|
||||||
|
diff --git a/src/plugins/platforms/cocoa/qcocoakeymapper.mm b/src/plugins/platforms/cocoa/qcocoakeymapper.mm
|
||||||
|
index 350ae4b9be..457bb3408d 100644
|
||||||
|
--- a/src/plugins/platforms/cocoa/qcocoakeymapper.mm
|
||||||
|
+++ b/src/plugins/platforms/cocoa/qcocoakeymapper.mm
|
||||||
|
@@ -462,7 +462,8 @@ QList<int> QCocoaKeyMapper::possibleKeys(const QKeyEvent *event) const
|
||||||
|
Qt::KeyboardModifiers neededMods = ModsTbl[i];
|
||||||
|
int key = kbItem->qtKey[i];
|
||||||
|
if (key && key != baseKey && ((keyMods & neededMods) == neededMods)) {
|
||||||
|
- ret << int(key + (keyMods & ~neededMods));
|
||||||
|
+ // Patch: Fix non-english layout global shortcuts.
|
||||||
|
+ ret << int(key + neededMods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||||
|
index 597cfa8318..579d79734d 100644
|
||||||
|
--- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||||
|
+++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||||
|
@@ -96,12 +96,17 @@ QT_USE_NAMESPACE
|
||||||
|
|
||||||
|
@interface QT_MANGLE_NAMESPACE(QNSStatusItem) : NSObject <NSUserNotificationCenterDelegate>
|
||||||
|
@property (nonatomic, assign) QCocoaMenu *menu;
|
||||||
|
+// Patch: Create a rich os x tray icon (pixel-perfect, theme switching).
|
||||||
|
+@property (nonatomic, assign) bool menuVisible;
|
||||||
|
+@property (nonatomic, readonly) bool iconSelected;
|
||||||
|
@property (nonatomic, assign) QIcon icon;
|
||||||
|
@property (nonatomic, readonly) NSStatusItem *item;
|
||||||
|
@property (nonatomic, readonly) QRectF geometry;
|
||||||
|
- (instancetype)initWithSysTray:(QCocoaSystemTrayIcon *)systray;
|
||||||
|
- (void)triggerSelector:(id)sender button:(Qt::MouseButton)mouseButton;
|
||||||
|
- (void)doubleClickSelector:(id)sender;
|
||||||
|
+- (void)setIconSelected:(bool)selected;
|
||||||
|
+- (bool)hasMenu;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface QT_MANGLE_NAMESPACE(QNSImageView) : NSImageView
|
||||||
|
@@ -173,7 +178,10 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon)
|
||||||
|
// (device independent pixels). The menu height on past and
|
||||||
|
// current OS X versions is 22 points. Provide some future-proofing
|
||||||
|
// by deriving the icon height from the menu height.
|
||||||
|
- const int padding = 4;
|
||||||
|
+
|
||||||
|
+ // Patch: Create a rich os x tray icon (pixel-perfect, theme switching).
|
||||||
|
+ const int padding = 0;
|
||||||
|
+
|
||||||
|
const int menuHeight = [[NSStatusBar systemStatusBar] thickness];
|
||||||
|
const int maxImageHeight = menuHeight - padding;
|
||||||
|
|
||||||
|
@@ -183,8 +191,12 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon)
|
||||||
|
// devicePixelRatio for the "best" screen on the system.
|
||||||
|
qreal devicePixelRatio = qApp->devicePixelRatio();
|
||||||
|
const int maxPixmapHeight = maxImageHeight * devicePixelRatio;
|
||||||
|
+
|
||||||
|
+ // Patch: Create a rich os x tray icon (pixel-perfect, theme switching).
|
||||||
|
+ const QIcon::Mode mode = m_sys->item.iconSelected ? QIcon::Selected : QIcon::Normal;
|
||||||
|
+
|
||||||
|
QSize selectedSize;
|
||||||
|
- Q_FOREACH (const QSize& size, sortByHeight(icon.availableSizes())) {
|
||||||
|
+ Q_FOREACH (const QSize& size, sortByHeight(icon.availableSizes(mode))) {
|
||||||
|
// Select a pixmap based on the height. We want the largest pixmap
|
||||||
|
// with a height smaller or equal to maxPixmapHeight. The pixmap
|
||||||
|
// may rectangular; assume it has a reasonable size. If there is
|
||||||
|
@@ -200,9 +212,9 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon)
|
||||||
|
|
||||||
|
// Handle SVG icons, which do not return anything for availableSizes().
|
||||||
|
if (!selectedSize.isValid())
|
||||||
|
- selectedSize = icon.actualSize(QSize(maxPixmapHeight, maxPixmapHeight));
|
||||||
|
+ selectedSize = icon.actualSize(QSize(maxPixmapHeight, maxPixmapHeight), mode);
|
||||||
|
|
||||||
|
- QPixmap pixmap = icon.pixmap(selectedSize);
|
||||||
|
+ QPixmap pixmap = icon.pixmap(selectedSize, mode);
|
||||||
|
|
||||||
|
// Draw a low-resolution icon if there is not enough pixels for a retina
|
||||||
|
// icon. This prevents showing a small icon on retina displays.
|
||||||
|
@@ -301,6 +313,10 @@ QT_END_NAMESPACE
|
||||||
|
{
|
||||||
|
self.down = NO;
|
||||||
|
|
||||||
|
+ // Patch: Create a rich os x tray icon (pixel-perfect, theme switching).
|
||||||
|
+ [self.parent setIconSelected:false];
|
||||||
|
+ self.parent.menuVisible = false;
|
||||||
|
+
|
||||||
|
[self setNeedsDisplay:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -310,6 +326,9 @@ QT_END_NAMESPACE
|
||||||
|
int clickCount = [mouseEvent clickCount];
|
||||||
|
[self setNeedsDisplay:YES];
|
||||||
|
|
||||||
|
+ // Patch: Create a rich os x tray icon (pixel-perfect, theme switching).
|
||||||
|
+ [self.parent setIconSelected:((clickCount != 2) && [self.parent hasMenu])];
|
||||||
|
+
|
||||||
|
if (clickCount == 2) {
|
||||||
|
[self menuTrackingDone:nil];
|
||||||
|
[self.parent doubleClickSelector:self];
|
||||||
|
@@ -326,6 +345,10 @@ QT_END_NAMESPACE
|
||||||
|
- (void)mouseUp:(NSEvent *)mouseEvent
|
||||||
|
{
|
||||||
|
Q_UNUSED(mouseEvent);
|
||||||
|
+
|
||||||
|
+ // Patch: Create a rich os x tray icon (pixel-perfect, theme switching).
|
||||||
|
+ [self.parent setIconSelected:false];
|
||||||
|
+
|
||||||
|
[self menuTrackingDone:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -337,6 +360,10 @@ QT_END_NAMESPACE
|
||||||
|
- (void)rightMouseUp:(NSEvent *)mouseEvent
|
||||||
|
{
|
||||||
|
Q_UNUSED(mouseEvent);
|
||||||
|
+
|
||||||
|
+ // Patch: Create a rich os x tray icon (pixel-perfect, theme switching).
|
||||||
|
+ [self.parent setIconSelected:false];
|
||||||
|
+
|
||||||
|
[self menuTrackingDone:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -352,7 +379,8 @@ QT_END_NAMESPACE
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)drawRect:(NSRect)rect {
|
||||||
|
- [[self.parent item] drawStatusBarBackgroundInRect:rect withHighlight:self.down];
|
||||||
|
+ // Patch: Create a rich os x tray icon (pixel-perfect, theme switching).
|
||||||
|
+ [[self.parent item] drawStatusBarBackgroundInRect:rect withHighlight:([self.parent hasMenu] && self.down)];
|
||||||
|
[super drawRect:rect];
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
@@ -372,6 +400,10 @@ QT_END_NAMESPACE
|
||||||
|
if (self) {
|
||||||
|
item = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
|
||||||
|
menu = nullptr;
|
||||||
|
+
|
||||||
|
+ // Patch: Create a rich os x tray icon (pixel-perfect, theme switching).
|
||||||
|
+ self.menuVisible = false;
|
||||||
|
+
|
||||||
|
systray = sys;
|
||||||
|
imageCell = [[QNSImageView alloc] initWithParent:self];
|
||||||
|
[item setView: imageCell];
|
||||||
|
@@ -382,6 +414,11 @@ QT_END_NAMESPACE
|
||||||
|
- (void)dealloc {
|
||||||
|
[[NSStatusBar systemStatusBar] removeStatusItem:item];
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:imageCell];
|
||||||
|
+
|
||||||
|
+ // Patch: Fix crash in macOS 10.14.
|
||||||
|
+ // Somehow item and imageCell are retained and attempt to be drawn if left in view.
|
||||||
|
+ [item setView: nil];
|
||||||
|
+
|
||||||
|
imageCell.parent = nil;
|
||||||
|
[imageCell release];
|
||||||
|
[item release];
|
||||||
|
@@ -416,6 +453,10 @@ QT_END_NAMESPACE
|
||||||
|
selector:@selector(menuTrackingDone:)
|
||||||
|
name:NSMenuDidEndTrackingNotification
|
||||||
|
object:m];
|
||||||
|
+
|
||||||
|
+ // Patch: Create a rich os x tray icon (pixel-perfect, theme switching).
|
||||||
|
+ self.menuVisible = true;
|
||||||
|
+
|
||||||
|
[item popUpStatusItemMenu: m];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -427,6 +468,15 @@ QT_END_NAMESPACE
|
||||||
|
emit systray->activated(QPlatformSystemTrayIcon::DoubleClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
+- (void)setIconSelected:(bool)selected {
|
||||||
|
+ _iconSelected = selected;
|
||||||
|
+ systray->updateIcon(icon);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+- (bool)hasMenu {
|
||||||
|
+ return menu != nil;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification {
|
||||||
|
Q_UNUSED(center);
|
||||||
|
Q_UNUSED(notification);
|
||||||
|
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
|
||||||
|
index 3008a056a2..d98eade4a3 100644
|
||||||
|
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
|
||||||
|
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
|
||||||
|
@@ -499,6 +499,15 @@ NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags)
|
||||||
|
// Select base window type. Note that the value of NSBorderlessWindowMask is 0.
|
||||||
|
NSUInteger styleMask = (frameless || !resizable) ? NSWindowStyleMaskBorderless : NSWindowStyleMaskResizable;
|
||||||
|
|
||||||
|
+ // Patch: allow creating panels floating on all spaces in macOS.
|
||||||
|
+ // If you call "setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary" before
|
||||||
|
+ // setting the "NSWindowStyleMaskNonactivatingPanel" bit in the style mask it won't work after that.
|
||||||
|
+ // So we need a way to set that bit before Qt sets collection behavior the way it does.
|
||||||
|
+ QVariant nonactivatingPanelMask = window()->property("_td_macNonactivatingPanelMask");
|
||||||
|
+ if (nonactivatingPanelMask.isValid() && nonactivatingPanelMask.toBool()) {
|
||||||
|
+ styleMask |= NSWindowStyleMaskNonactivatingPanel;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
if (frameless) {
|
||||||
|
// No further customizations for frameless since there are no window decorations.
|
||||||
|
} else if (flags & Qt::CustomizeWindowHint) {
|
||||||
|
diff --git a/src/plugins/platforms/cocoa/qnsview_keys.mm b/src/plugins/platforms/cocoa/qnsview_keys.mm
|
||||||
|
index ad751279bb..9a9d19693e 100644
|
||||||
|
--- a/src/plugins/platforms/cocoa/qnsview_keys.mm
|
||||||
|
+++ b/src/plugins/platforms/cocoa/qnsview_keys.mm
|
||||||
|
@@ -86,21 +86,29 @@
|
||||||
|
quint32 nativeVirtualKey = [nsevent keyCode];
|
||||||
|
|
||||||
|
QChar ch = QChar::ReplacementCharacter;
|
||||||
|
- int keyCode = Qt::Key_unknown;
|
||||||
|
-
|
||||||
|
- // If a dead key occurs as a result of pressing a key combination then
|
||||||
|
- // characters will have 0 length, but charactersIgnoringModifiers will
|
||||||
|
- // have a valid character in it. This enables key combinations such as
|
||||||
|
- // ALT+E to be used as a shortcut with an English keyboard even though
|
||||||
|
- // pressing ALT+E will give a dead key while doing normal text input.
|
||||||
|
- if ([characters length] != 0 || [charactersIgnoringModifiers length] != 0) {
|
||||||
|
- auto ctrlOrMetaModifier = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta) ? Qt::ControlModifier : Qt::MetaModifier;
|
||||||
|
- if (((modifiers & ctrlOrMetaModifier) || (modifiers & Qt::AltModifier)) && ([charactersIgnoringModifiers length] != 0))
|
||||||
|
- ch = QChar([charactersIgnoringModifiers characterAtIndex:0]);
|
||||||
|
- else if ([characters length] != 0)
|
||||||
|
- ch = QChar([characters characterAtIndex:0]);
|
||||||
|
- keyCode = [self convertKeyCode:ch];
|
||||||
|
- }
|
||||||
|
+
|
||||||
|
+ // Patch: Fix Alt+.. shortcuts in OS X. See https://bugreports.qt.io/browse/QTBUG-42584 at the end.
|
||||||
|
+ if ([characters length] != 0)
|
||||||
|
+ ch = QChar([characters characterAtIndex:0]);
|
||||||
|
+ else if ([charactersIgnoringModifiers length] != 0 && ((modifiers & Qt::MetaModifier) || (modifiers & Qt::AltModifier)))
|
||||||
|
+ ch = QChar([charactersIgnoringModifiers characterAtIndex:0]);
|
||||||
|
+
|
||||||
|
+ int keyCode = [self convertKeyCode:ch];
|
||||||
|
+ // int keyCode = Qt::Key_unknown;
|
||||||
|
+
|
||||||
|
+ // // If a dead key occurs as a result of pressing a key combination then
|
||||||
|
+ // // characters will have 0 length, but charactersIgnoringModifiers will
|
||||||
|
+ // // have a valid character in it. This enables key combinations such as
|
||||||
|
+ // // ALT+E to be used as a shortcut with an English keyboard even though
|
||||||
|
+ // // pressing ALT+E will give a dead key while doing normal text input.
|
||||||
|
+ // if ([characters length] != 0 || [charactersIgnoringModifiers length] != 0) {
|
||||||
|
+ // auto ctrlOrMetaModifier = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta) ? Qt::ControlModifier : Qt::MetaModifier;
|
||||||
|
+ // if (((modifiers & ctrlOrMetaModifier) || (modifiers & Qt::AltModifier)) && ([charactersIgnoringModifiers length] != 0))
|
||||||
|
+ // ch = QChar([charactersIgnoringModifiers characterAtIndex:0]);
|
||||||
|
+ // else if ([characters length] != 0)
|
||||||
|
+ // ch = QChar([characters characterAtIndex:0]);
|
||||||
|
+ // keyCode = [self convertKeyCode:ch];
|
||||||
|
+ // }
|
||||||
|
|
||||||
|
// we will send a key event unless the input method sets m_sendKeyEvent to false
|
||||||
|
m_sendKeyEvent = true;
|
||||||
|
@@ -196,6 +204,23 @@
|
||||||
|
[super keyUp:nsevent];
|
||||||
|
}
|
||||||
|
|
||||||
|
+// Patch: Enable Ctrl+Tab and Ctrl+Shift+Tab / Ctrl+Backtab handle in-app.
|
||||||
|
+- (BOOL)performKeyEquivalent:(NSEvent *)nsevent
|
||||||
|
+{
|
||||||
|
+ NSString *chars = [nsevent charactersIgnoringModifiers];
|
||||||
|
+
|
||||||
|
+ if ([nsevent type] == NSKeyDown && [chars length] > 0) {
|
||||||
|
+ QChar ch = [chars characterAtIndex:0];
|
||||||
|
+ Qt::Key qtKey = qt_mac_cocoaKey2QtKey(ch);
|
||||||
|
+ if ([nsevent modifierFlags] & NSControlKeyMask
|
||||||
|
+ && (qtKey == Qt::Key_Tab || qtKey == Qt::Key_Backtab)) {
|
||||||
|
+ [self handleKeyEvent:nsevent eventType:int(QEvent::KeyPress)];
|
||||||
|
+ return YES;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ return [super performKeyEquivalent:nsevent];
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
- (void)cancelOperation:(id)sender
|
||||||
|
{
|
||||||
|
Q_UNUSED(sender);
|
||||||
|
diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
|
||||||
|
index 9de3268fc8..8b281c95db 100644
|
||||||
|
--- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
|
||||||
|
+++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
|
||||||
|
@@ -1179,7 +1179,14 @@ void QWindowsNativeFileDialogBase::selectFile(const QString &fileName) const
|
||||||
|
// Hack to prevent CLSIDs from being set as file name due to
|
||||||
|
// QFileDialogPrivate::initialSelection() being QString-based.
|
||||||
|
if (!isClsid(fileName))
|
||||||
|
- m_fileDialog->SetFileName((wchar_t*)fileName.utf16());
|
||||||
|
+ // Patch: Fix handle of full fileName.
|
||||||
|
+ {
|
||||||
|
+ QString file = QDir::toNativeSeparators(fileName);
|
||||||
|
+ int lastBackSlash = file.lastIndexOf(QChar::fromLatin1('\\'));
|
||||||
|
+ if (lastBackSlash >= 0)
|
||||||
|
+ file = file.mid(lastBackSlash + 1);
|
||||||
|
+ m_fileDialog->SetFileName((wchar_t*)file.utf16());;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the index of the selected filter, accounting for QFileDialog
|
||||||
|
@@ -1456,7 +1463,8 @@ static QString createTemporaryItemCopy(QWindowsShellItem &qItem, QString *errorM
|
||||||
|
static QUrl itemToDialogUrl(QWindowsShellItem &qItem, QString *errorMessage)
|
||||||
|
{
|
||||||
|
QUrl url = qItem.url();
|
||||||
|
- if (url.isLocalFile() || url.scheme().startsWith(QLatin1String("http")))
|
||||||
|
+ // Patch: Make loaded 'http' resources copy.
|
||||||
|
+ if (url.isLocalFile()/*|| url.scheme().startsWith(QLatin1String("http"))*/)
|
||||||
|
return url;
|
||||||
|
const QString path = qItem.path();
|
||||||
|
if (path.isEmpty() && !qItem.isDir() && qItem.canStream()) {
|
||||||
|
diff --git a/src/plugins/platforms/windows/qwindowsservices.cpp b/src/plugins/platforms/windows/qwindowsservices.cpp
|
||||||
|
index 9504513a5e..811f3d62bd 100644
|
||||||
|
--- a/src/plugins/platforms/windows/qwindowsservices.cpp
|
||||||
|
+++ b/src/plugins/platforms/windows/qwindowsservices.cpp
|
||||||
|
@@ -125,6 +125,10 @@ static inline bool launchMail(const QUrl &url)
|
||||||
|
command.prepend(doubleQuote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ // Patch: Fix mail launch if no param is expected in this command.
|
||||||
|
+ if (command.indexOf(QStringLiteral("%1")) < 0) return false;
|
||||||
|
+
|
||||||
|
// Pass the url as the parameter. Should use QProcess::startDetached(),
|
||||||
|
// but that cannot handle a Windows command line [yet].
|
||||||
|
command.replace(QLatin1String("%1"), url.toString(QUrl::FullyEncoded));
|
||||||
|
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
|
||||||
|
index 7d511bf0d7..da3879cb56 100644
|
||||||
|
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
|
||||||
|
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
|
||||||
|
@@ -1351,7 +1351,8 @@ void QWindowsWindow::destroyWindow()
|
||||||
|
for (QWindow *w : tlw) {
|
||||||
|
if (w->transientParent() == window()) {
|
||||||
|
if (QWindowsWindow *tw = QWindowsWindow::windowsWindowOf(w))
|
||||||
|
- tw->updateTransientParent();
|
||||||
|
+ // Patch: Fix possibility of add / remove taskbar icon of the window.
|
||||||
|
+ tw->clearTransientParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QWindowsContext *context = QWindowsContext::instance();
|
||||||
|
@@ -1579,6 +1580,19 @@ void QWindowsWindow::updateTransientParent() const
|
||||||
|
// window is found, which can cause issues with modality. Loop up to top level.
|
||||||
|
while (newTransientParent && (GetWindowLongPtr(newTransientParent, GWL_STYLE) & WS_CHILD) != 0)
|
||||||
|
newTransientParent = GetParent(newTransientParent);
|
||||||
|
+ // Patch: Fix possibility of add / remove taskbar icon of the window.
|
||||||
|
+ if (newTransientParent && newTransientParent != oldTransientParent)
|
||||||
|
+ SetWindowLongPtr(m_data.hwnd, GWL_HWNDPARENT, (LONG_PTR)newTransientParent);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// Patch: Fix possibility of add / remove taskbar icon of the window.
|
||||||
|
+void QWindowsWindow::clearTransientParent() const
|
||||||
|
+{
|
||||||
|
+ if (window()->type() == Qt::Popup)
|
||||||
|
+ return; // QTBUG-34503, // a popup stays on top, no parent, see also WindowCreationData::fromWindow().
|
||||||
|
+ // Update transient parent.
|
||||||
|
+ const HWND oldTransientParent = GetWindow(m_data.hwnd, GW_OWNER);
|
||||||
|
+ HWND newTransientParent = 0;
|
||||||
|
|
||||||
|
if (newTransientParent != oldTransientParent)
|
||||||
|
SetWindowLongPtr(m_data.hwnd, GWL_HWNDPARENT, LONG_PTR(newTransientParent));
|
||||||
|
diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h
|
||||||
|
index ce67e46df3..a60edc151f 100644
|
||||||
|
--- a/src/plugins/platforms/windows/qwindowswindow.h
|
||||||
|
+++ b/src/plugins/platforms/windows/qwindowswindow.h
|
||||||
|
@@ -353,6 +353,10 @@ private:
|
||||||
|
inline void setWindowState_sys(Qt::WindowStates newState);
|
||||||
|
inline void setParent_sys(const QPlatformWindow *parent);
|
||||||
|
inline void updateTransientParent() const;
|
||||||
|
+
|
||||||
|
+ // Patch: Fix possibility of add / remove taskbar icon of the window.
|
||||||
|
+ inline void clearTransientParent() const;
|
||||||
|
+
|
||||||
|
void destroyWindow();
|
||||||
|
inline bool isDropSiteEnabled() const { return m_dropTarget != 0; }
|
||||||
|
void setDropSiteEnabled(bool enabled);
|
||||||
|
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp
|
||||||
|
index bf339ca5c5..4cdf9189ad 100644
|
||||||
|
--- a/src/widgets/kernel/qwidget.cpp
|
||||||
|
+++ b/src/widgets/kernel/qwidget.cpp
|
||||||
|
@@ -5161,6 +5161,17 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset,
|
||||||
|
return; // Fully transparent.
|
||||||
|
|
||||||
|
Q_D(QWidget);
|
||||||
|
+
|
||||||
|
+ // Patch: save and restore dirtyOpaqueChildren field.
|
||||||
|
+ //
|
||||||
|
+ // Just like in QWidget::grab() this field should be restored
|
||||||
|
+ // after the d->render() call, because it will be set to 1 and
|
||||||
|
+ // opaqueChildren field will be filled with empty region in
|
||||||
|
+ // case the widget is hidden (because all the opaque children
|
||||||
|
+ // will be skipped in isVisible() check).
|
||||||
|
+ //
|
||||||
|
+ const bool oldDirtyOpaqueChildren = d->dirtyOpaqueChildren;
|
||||||
|
+
|
||||||
|
const bool inRenderWithPainter = d->extra && d->extra->inRenderWithPainter;
|
||||||
|
const QRegion toBePainted = !inRenderWithPainter ? d->prepareToRender(sourceRegion, renderFlags)
|
||||||
|
: sourceRegion;
|
||||||
|
@@ -5182,6 +5193,10 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset,
|
||||||
|
if (!inRenderWithPainter && (opacity < 1.0 || (target->devType() == QInternal::Printer))) {
|
||||||
|
d->render_helper(painter, targetOffset, toBePainted, renderFlags);
|
||||||
|
d->extra->inRenderWithPainter = inRenderWithPainter;
|
||||||
|
+
|
||||||
|
+ // Patch: save and restore dirtyOpaqueChildren field.
|
||||||
|
+ d->dirtyOpaqueChildren = oldDirtyOpaqueChildren;
|
||||||
|
+
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -5214,6 +5229,9 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset,
|
||||||
|
d->setSharedPainter(oldPainter);
|
||||||
|
|
||||||
|
d->extra->inRenderWithPainter = inRenderWithPainter;
|
||||||
|
+
|
||||||
|
+ // Patch: save and restore dirtyOpaqueChildren field.
|
||||||
|
+ d->dirtyOpaqueChildren = oldDirtyOpaqueChildren;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sendResizeEvents(QWidget *target)
|
||||||
|
@@ -8968,7 +8986,8 @@ bool QWidget::event(QEvent *event)
|
||||||
|
case QEvent::KeyPress: {
|
||||||
|
QKeyEvent *k = (QKeyEvent *)event;
|
||||||
|
bool res = false;
|
||||||
|
- if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier?
|
||||||
|
+ // Patch: Enable Ctrl+Tab and Ctrl+Shift+Tab / Ctrl+Backtab handle in-app.
|
||||||
|
+ if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier))) {
|
||||||
|
if (k->key() == Qt::Key_Backtab
|
||||||
|
|| (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier)))
|
||||||
|
res = focusNextPrevChild(false);
|
||||||
|
diff --git a/src/widgets/util/qsystemtrayicon_qpa.cpp b/src/widgets/util/qsystemtrayicon_qpa.cpp
|
||||||
|
index c0bf058681..1c8b627d01 100644
|
||||||
|
--- a/src/widgets/util/qsystemtrayicon_qpa.cpp
|
||||||
|
+++ b/src/widgets/util/qsystemtrayicon_qpa.cpp
|
||||||
|
@@ -93,6 +93,10 @@ void QSystemTrayIconPrivate::updateMenu_sys()
|
||||||
|
if (qpa_sys && menu) {
|
||||||
|
addPlatformMenu(menu);
|
||||||
|
qpa_sys->updateMenu(menu->platformMenu());
|
||||||
|
+
|
||||||
|
+ // Patch: Create a rich os x tray icon (pixel-perfect, theme switching).
|
||||||
|
+ } else if (qpa_sys) {
|
||||||
|
+ qpa_sys->updateMenu(nullptr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
diff --git a/src/widgets/widgets/qabstractscrollarea.cpp b/src/widgets/widgets/qabstractscrollarea.cpp
|
||||||
|
index 598d173144..fd2e636563 100644
|
||||||
|
--- a/src/widgets/widgets/qabstractscrollarea.cpp
|
||||||
|
+++ b/src/widgets/widgets/qabstractscrollarea.cpp
|
||||||
|
@@ -655,15 +655,21 @@ scrolling range.
|
||||||
|
QSize QAbstractScrollArea::maximumViewportSize() const
|
||||||
|
{
|
||||||
|
Q_D(const QAbstractScrollArea);
|
||||||
|
- int hsbExt = d->hbar->sizeHint().height();
|
||||||
|
- int vsbExt = d->vbar->sizeHint().width();
|
||||||
|
+ // Patch: Count the sizeHint of the bar only if it is displayed.
|
||||||
|
+ //int hsbExt = d->hbar->sizeHint().height();
|
||||||
|
+ //int vsbExt = d->vbar->sizeHint().width();
|
||||||
|
|
||||||
|
int f = 2 * d->frameWidth;
|
||||||
|
QSize max = size() - QSize(f + d->left + d->right, f + d->top + d->bottom);
|
||||||
|
- if (d->vbarpolicy == Qt::ScrollBarAlwaysOn)
|
||||||
|
+ // Patch: Count the sizeHint of the bar only if it is displayed.
|
||||||
|
+ if (d->vbarpolicy == Qt::ScrollBarAlwaysOn) {
|
||||||
|
+ int vsbExt = d->vbar->sizeHint().width();
|
||||||
|
max.rwidth() -= vsbExt;
|
||||||
|
- if (d->hbarpolicy == Qt::ScrollBarAlwaysOn)
|
||||||
|
+ }
|
||||||
|
+ if (d->hbarpolicy == Qt::ScrollBarAlwaysOn) {
|
||||||
|
+ int hsbExt = d->hbar->sizeHint().height();
|
||||||
|
max.rheight() -= hsbExt;
|
||||||
|
+ }
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Add table
Reference in a new issue