
Kerangka Kustomisasi Jendela Cross-Platform untuk Widget QT dan QT Quick. Mendukung Windows, Linux dan MacOS.
Anda dapat bergabung dengan saluran Discord kami untuk berkomunikasi dengan kami. Anda dapat membagikan temuan, pemikiran, dan ide Anda tentang peningkatan / penerapan fungsionalitas framelesshelper pada lebih banyak platform dan aplikasi!
FramelessDialog . Anda dapat menggunakannya jika QDialog lebih disukai daripada QWidget umum.WindowBorderPainter yang baru diperkenalkan, dan juga diekspos di depan umum, sehingga Anda dapat mengubah cara kami menggambar perbatasan jendela dengan mudah.WindowBorder . Ini adalah dekorator perbatasan jendela lintas platform, dan dapat bekerja tanpa elemen FramelessHelper .FramelessApplicationWindow . Ini adalah pembungkus sederhana dari elemen ApplicationWindow standar, cukup lepaskan bilah judul dan tambahkan perbatasan jendela.find_package untuk menemukan framelesshelper.






Editor Vogen Menggunakan Kerangka Kerja Qsynthesis . URL Repositori: https://gitee.com/functioner/qvogenclient.
Ada beberapa batasan tambahan untuk setiap platform, silakan merujuk ke bagian Catatan Platform di bawah ini.
git clone --recursive https://github.com/wangwenx190/framelesshelper.git # "--recursive" is necessary to clone the submodules.
mkdir build # Please change to your own build directory!
cd build
cmake -DCMAKE_PREFIX_PATH= < YOUR_QT_SDK_DIR_PATH > -DCMAKE_INSTALL_PREFIX= < WHERE_YOU_WANT_TO_INSTALL > -DCMAKE_BUILD_TYPE=Release -GNinja < PATH_TO_THE_REPOSITORY >
cmake --build . --config Release --target all --parallel
cmake --install . --config Release --strip # Don't add "--strip" for MSVC/Clang-CL/Intel-CL toolchains!
# YOUR_QT_SDK_DIR_PATH: the Qt SDK directory, something like "C:/Qt/6.5.1/msvc2019_64" or "/opt/Qt/6.5.1/gcc_64". Please change to your own path!
# WHERE_YOU_WANT_TO_INSTALL: the install directory of FramelessHelper, something like "../install". You can ignore this setting if you don't need to install the CMake package. Please change to your own path!
# PATH_TO_THE_REPOSITORY: the source code directory of FramelessHelper, something like "../framelesshelper". Please change to your own path! Anda juga dapat menggunakan Qt6_DIR atau Qt5_DIR untuk menggantikan CMAKE_PREFIX_PATH :
cmake -DQt6_DIR=C:/Qt/6.5.1/msvc2019_64/lib/cmake/Qt6 [other parameters ...]
# Or
cmake -DQt5_DIR=C:/Qt/5.15.2/msvc2019_64/lib/cmake/Qt5 [other parameters ...] Jika ada kesalahan saat mengkloning submodul, coba jalankan git submodule update --init --recursive --remote di direktori proyek, perintah itu akan mengunduh & memperbarui semua submodul. Jika gagal lagi, coba jalankan beberapa kali sampai akhirnya berhasil.
Setelah kompilasi dan instalasi selesai, Anda akan dapat menggunakan perintah find_package(FramelessHelper REQUIRED COMPONENTS Core Widgets Quick) untuk menemukan dan menautkan ke pustaka framelesshelper. Tetapi sebelum melakukan itu, pastikan CMake tahu di mana menemukan FramelessHelper, dengan melewati variabel CMAKE_PREFIX_PATH atau FramelessHelper_DIR ke sana. Sebagai contoh: -DCMAKE_PREFIX_PATH=C:/my-cmake-packages;C:/my-toolchain;etc... atau -DFramelessHelper_DIR=C:/Projects/FramelessHelper/lib64/cmake/FramelessHelper . Bangun FramelessHelper sebagai sub-direktori proyek CMake Anda tentu saja juga didukung. Nama target framelesshelper yang didukung adalah FramelessHelper::Core , FramelessHelper::Widgets dan FramelessHelper::Quick . Contoh kode:
# Find Qt:
find_package (QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
find_package (Qt ${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
# Find FramelessHelper:
find_package (FramelessHelper REQUIRED COMPONENTS Core Widgets)
# Create your target:
add_executable (demo)
# Add your source code:
target_sources (demo PRIVATE main.cpp)
# Link to Qt and FramelessHelper:
target_link_libraries (demo PRIVATE
Qt ${QT_VERSION_MAJOR} ::Widgets
FramelessHelper::Core
FramelessHelper::Widgets
) Jika Anda memerlukan sintaksis sintaks dari modul cepat FramelessHelper, silakan atur variabel QML_IMPORT_PATH . Contoh kode:
# This is the path where you want FramelessHelper's Quick plugin (it only contains the QML meta
# info and an optional dummy library, for QtCreator's QML tooling purpose, it's not the Quick
# module) to place. Please change to your own path!
# If you are using add_subdirectory() to include FramelessHelper directly, you can change it to
# "${PROJECT_BINARY_DIR}/imports" instead of the install location.
set (FRAMELESSHELPER_IMPORT_DIR "C:/packages/FramelessHelper/qml" )
list ( APPEND QML_IMPORT_PATH " ${FRAMELESSHELPER_IMPORT_DIR} " )
list ( REMOVE_DUPLICATES QML_IMPORT_PATH)
# Force cache refresh:
set (QML_IMPORT_PATH ${QML_IMPORT_PATH} CACHE STRING "Qt Creator extra QML import paths" FORCE) Untuk menyesuaikan bingkai jendela QWIDGET, Anda perlu membuat instantiate objek FramelessWidgetsHelper dan kemudian memasangnya ke widget level atas widget, dan kemudian FramelessWidgetsHelper akan melakukan semua pekerjaan sisanya untuk Anda: bingkai jendela akan dihapus secara otomatis setelah dilampirkan ke widget tingkat atas dengan sukses. Secara teori Anda dapat membuat beberapa objek FramelessWidgetsHelper untuk widget yang sama, dalam hal ini hanya akan ada satu objek yang tetap fungsional, semua objek lain akan menjadi pembungkus yang satu itu. Tetapi untuk memastikan semuanya berjalan lancar dan normal, Anda tidak boleh melakukannya dalam hal apa pun. Cara paling sederhana untuk membuat objek FramelessWidgetsHelper adalah dengan memanggil metode statis FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *) . Ini akan mengembalikan pegangan objek yang sebelumnya dipakai jika ada, atau akan instantiate objek baru jika tidak dapat menemukannya. Aman untuk memanggil metode ini beberapa kali untuk widget yang sama, itu tidak akan membuat instantiasi objek baru jika sudah ada. Ini juga tidak masalah kapan dan di mana Anda memanggil fungsi itu selama widget tingkat atas adalah sama. Objek yang dibuat secara internal akan selalu diarahkan ke widget tingkat atas. Setelah Anda mendapatkan pegangan dari Objek FramelessWidgetsHelper , Anda dapat memanggil void FramelessWidgetsHelper::extendsContentIntoTitleBar() untuk membiarkannya menyembunyikan bilah judul default yang disediakan oleh sistem operasi. Untuk memastikan FramelessWidgetsHelper dapat menemukan widget tingkat atas yang benar, Anda harus memanggil FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *) berfungsi pada widget yang memiliki rantai induk lengkap yang root induknya adalah widget tingkat atas. Untuk membuat jendela tanpa bingkai dapat diseret, Anda harus memberikan widget judul buatan sendiri, widget judul bar tidak perlu dalam kondisi persegi panjang, itu juga tidak perlu ditempatkan di baris pertama jendela. Hubungi void FramelessWidgetsHelper::setTitleBarWidget(QWidget *) untuk memberi tahu FramelessHelper , apa widget judul bar Anda. Secara default, semua widget di area bilah judul tidak akan bertanggung jawab atas acara mouse dan keyboard karena mereka telah dicegat oleh FramelessHelper. Untuk membuat mereka memulihkan keadaan yang bertanggung jawab, Anda harus membuat mereka terlihat untuk melakukan tes. Hubungi void FramelessWidgetsHelper::setHitTestVisible(QWidget* ) untuk melakukan itu. Anda tentu saja dapat menyebutnya di widget yang tidak ada di dalam judul bilah sama sekali, itu tidak akan memiliki efek apa pun. Karena keterbatasan QT sendiri, Anda perlu memastikan widget Anda memiliki rantai induk lengkap yang root induknya adalah widget tingkat atas. Jangan pernah mencoba menghapus objek FramelessWidgetsHelper , mungkin masih memantau dan mengendalikan widget Anda, dan QT akan menghapusnya untuk Anda secara otomatis. Tidak perlu khawatir tentang kebocoran memori.
Ada juga dua kelas yang disebut FramelessWidget dan FramelessMainWindow , mereka hanya pembungkus sederhana dari FramelessWidgetsHelper , yang hanya menyimpan panggilan fungsi void FramelessWidgetsHelper::extendsContentIntoTitleBar() untuk Anda. Anda benar -benar dapat menggunakan QWidget biasa.
Pertama -tama, panggilan void FramelessHelper::Widgets::initialize() dalam fungsi main Anda pada tahap yang sangat awal ( harus sebelum pembangunan objek Q(Gui|Core)Application ):
int main ( int , char **)
{
FramelessHelper::Widgets::initialize ();
// ...
}Kemudian sembunyikan bilah judul standar yang disediakan oleh OS:
MyWidget::MyWidget (QWidget *parent) : QWidget(parent)
{
// You should do this early enough.
FramelessWidgetsHelper::get ( this )-> extendsContentIntoTitleBar ();
// ...
} Kemudian beri tahu FramelessHelper apa yang seharusnya menjadi judul bilah:
void MyWidget::myFunction ()
{
// ...
FramelessWidgetsHelper::get ( this )-> setTitleBarWidget (m_myTitleBarWidget);
// ...
}Kemudian buat beberapa widget di dalam judul bar Anda terlihat untuk melakukan tes:
void MyWidget::myFunction2 ()
{
// ...
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someSearchBox);
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someButton);
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someMenuItem);
// ...
} Catatan penting untuk aplikasi widget QT : Beberapa fungsi mungkin hanya tersedia ketika FramelessHelper telah menyelesaikan proses kustomisasi jendela, seperti mengubah geometri/bendera/status jendela. Dalam hal ini Anda dapat terhubung ke sinyal public void ready() dari FramelessHelper untuk mendapatkan titik waktu yang akurat dan melakukan proses inisialisasi istirahat Anda sesudahnya.
Pertama -tama, Anda harus memanggil void FramelessHelper::Quick::initialize() dalam fungsi main Anda pada tahap yang sangat awal ( harus sebelum pembangunan objek Q(Gui|Core)Application ):
int main ( int , char **)
{
FramelessHelper::Quick::initialize ();
// ...
} Maka Anda perlu mendaftarkan tipe khusus yang disediakan oleh FramelessHelper dengan menelepon void FramelessHelper::Quick::registerTypes(QQmlEngine *) , sebelum mesin QML memuat dokumen QML apa pun:
int main ( int , char **)
{
// ...
QQmlApplicationEngine engine;
FramelessHelper::Quick::registerTypes (&engine);
// ...
} Sekarang Anda dapat menulis dokumen QML Anda. Anda harus mengimpor FramelessHelper dari org.wangwenx190.FramelessHelper . Anda harus menentukan nomor versi tepat setelah itu jika Anda menggunakan QT5:
import org.wangwenx190.FramelessHelper 1.0 // You can use "auto" or omit the version number in Qt6. Dan kemudian Anda dapat menggunakan properti terlampir dari QML Type FramelessHelper :
Window {
Item {
id : myTitleBar
Item { id : someControl1 }
Item { id : someControl2 }
Item { id : someControl3 }
Component . onCompleted : {
// Don't access FramelessHelper too early, otherwise it may not be able to find the root window!
FramelessHelper . titleBarItem = myTitleBar;
FramelessHelper . setHitTestVisible (someControl1);
FramelessHelper . setHitTestVisible (someControl2);
FramelessHelper . setHitTestVisible (someControl3);
}
}
} Itu sama dengan antarmuka FramelessWidgetsHelper , QML Type FramelessHelper hanya akan dipakai sekali untuk setiap Window , di mana pun dan di mana Anda menggunakan properti terlampir darinya. Namun, karena desain khusus dari tipe FramelessHelper , Anda juga dapat menggunakannya seperti tipe QML normal:
Window {
Item {
id : myTitleBar
Item { id : someControl1 }
Item { id : someControl2 }
Item { id : someControl3 }
Component . onCompleted : {
framelessHelper . setHitTestVisible (someControl1);
framelessHelper . setHitTestVisible (someControl2);
framelessHelper . setHitTestVisible (someControl3);
}
}
FramelessHelper {
id : framelessHelper
titleBarItem : myTitleBar
}
} Secara teori, dimungkinkan untuk membuat beberapa objek FramelessHelper untuk Window yang sama, dalam hal ini hanya satu dari mereka yang akan tetap fungsional, semua objek lain akan menjadi pembungkusnya, tetapi melakukan hal itu tidak disarankan dan dapat menyebabkan perilaku atau bug yang tidak terduga, jadi harap hindari mencoba untuk melakukannya dalam hal apa pun.
Jika Anda menemukan fungsi FramelessHelper tidak berpengaruh setelah menelepon, alasan yang paling mungkin adalah pada saat Anda memanggil fungsi/mengubah properti FramelessHelper , jendela root belum menyelesaikan proses inisialisasi dan dengan demikian FramelessHelper tidak dapat mendapatkan pegangannya, sehingga tindakan apa pun dari pengguna akan diabaikan sampai jendela root selesai inisialisasi.
Ada juga tipe QML yang disebut FramelessWindow , itu hanya pembungkus sederhana dari FramelessHelper , Anda benar -benar dapat menggunakan Window biasa sebagai gantinya.
Catatan Penting untuk Aplikasi Cepat QT : Beberapa fungsi mungkin hanya tersedia ketika FramelessHelper telah menyelesaikan proses kustomisasi jendela, seperti mengubah geometri/bendera/status jendela/status. Dalam hal ini Anda dapat terhubung ke sinyal public void ready() dari FramelessHelper untuk mendapatkan titik waktu yang akurat dan melakukan proses inisialisasi istirahat Anda setelah itu:
Window {
FramelessHelper . onReady : {
// do something here ...
}
} Window {
FramelessHelper {
onReady : {
// do something here ...
}
}
}Silakan merujuk ke proyek demo untuk melihat penggunaan yang lebih rinci: Contoh
Jika komposisi DWM dinonaktifkan dalam beberapa kasus yang sangat jarang (hanya mungkin pada Windows 7), sudut kiri atas dan sudut kanan atas akan muncul dalam bentuk bulat. Sudut bundar dapat dikembalikan ke persegi jika Anda mengaktifkan kembali komposisi DWM.
Ada bug driver OpenGL yang akan menyebabkan beberapa jendela tanpa bingkai memiliki bilah hitam aneh tepat di atas bilah judul buatan rumah Anda, dan itu juga membuat kontrol di jendela Anda bergeser ke sudut kanan bawah untuk beberapa piksel. Ini adalah bug driver kartu grafis Anda, khususnya, driver OpenGL Anda, bukan framelesshelper. Ada beberapa solusi yang diberikan oleh pengguna kami tetapi beberapa dari mereka mungkin tidak berfungsi dalam semua kondisi, Anda dapat memilih satu dari mereka:
| Larutan | Prinsip |
|---|---|
| Tingkatkan driver grafis | Cobalah untuk menggunakan driver yang lebih baru yang mungkin dikirimkan dengan perbaikan |
| Ubah tema sistem menjadi "dasar" (bertentangan dengan "windows aero") | Biarkan Windows menggunakan rendering perangkat lunak murni |
| Jika ada beberapa kartu grafis, gunakan yang lain sebagai gantinya | Cobalah untuk menggunakan driver yang berbeda yang mungkin tidak memiliki bug seperti itu |
| Tingkatkan sistem ke setidaknya Windows 11 | Windows 11 Designed Sistem Windowing sehingga bug tidak lagi dapat dipicu |
Hapus gaya WS_THICKFRAME dan WS_OVERLAPPED dari jendela, dan mungkin juga menambahkan gaya WS_POPUP secara bersamaan, dan jangan melakukan apa pun di dalam blok WM_NCCALCSIZE (cukup kembalikan false secara langsung atau hapus/komentari seluruh blok) | Cobalah untuk mencerminkan perilaku FramelessWindowHint QT |
Gunakan Qt::FramelessWindowHint alih -alih melakukan trik WM_NCCALCSIZE | Jalur kode rendering QT sama sekali berbeda antara kedua solusi ini |
| Paksa QT untuk menggunakan backend sudut alih -alih desktop opengl | Angle akan menerjemahkan arahan opengl ke dalam d3d |
| Paksa QT untuk menggunakan rendering perangkat lunak murni alih -alih rendering melalui OpenGL | QT tidak menggunakan OpenGL sama sekali |
| Paksa QT untuk menggunakan pustaka 3D mesa alih -alih opengl normal | Cobalah untuk menggunakan implementasi OpenGL yang berbeda |
| Gunakan Direct3D/Vulkan/Metal bukan OpenGL | Jangan gunakan buggy opengl |
Jika Anda cukup beruntung, salah satunya dapat memperbaiki masalah ini untuk Anda. Jika tidak, Anda dapat mencoba menggunakan beberapa solusi bersama. Tapi saya tidak dapat menjamin masalah ini dapat 100% diperbaiki.
Karena ada banyak sub-versi Windows 10, sangat disarankan untuk menggunakan versi terbaru Windows 10, setidaknya tidak lebih tua dari Windows 10 1809 . Jika Anda mencoba menggunakan kerangka kerja ini pada beberapa versi Windows 10 yang sangat lama seperti 1507 atau 1607, mungkin ada beberapa masalah kompatibilitas. Menggunakan kerangka kerja ini pada Windows 7 juga didukung tetapi tidak disarankan. Untuk mendapatkan perilaku paling stabil dan penampilan terbaik, Anda harus menggunakannya pada versi terbaru Windows 10 atau Windows 11.
Untuk membuat tata letak snap berfungsi seperti yang diharapkan, ada beberapa aturan tambahan untuk diikuti oleh tombol sistem buatan sendiri:
setSystemButton() untuk setiap tombol (dapat berupa QWIDGET atau QQUICKITEM ) untuk memberi tahu FramelessHelper mana yang merupakan tombol Minimalkan/Maksimalkan/Tutup.When running on Win10, it seems the top border is missing? But the demo applications still have it? FramelessHelper menyembunyikan bilah judul sistem dengan menghapus seluruh bagian atas bingkai jendela, termasuk perbatasan atas. Tidak ada cara untuk hanya menghapus bilah judul sistem tetapi masih mempertahankan perbatasan atas pada saat yang sama, bahkan Microsoft sendiri tidak dapat melakukannya juga. Alasan pasti tidak diketahui oleh pengembang non-Microsoft, dan saya tidak tertarik menggali semua keajaiban di baliknya. Jadi Anda harus menggambar sendiri secara manual untuk berpura -pura perbatasan atas masih ada. Anda dapat mengambil tinggi dan warnanya melalui API DWM resmi. Silakan merujuk ke dokumentasi DwmGetWindowAttribute() dan DwmGetColorizationColor() . Aplikasi demo masih memiliki batas atas karena semua jendela utama mereka mewarisi dari FramelessWidget atau FramelessMainWindow , yang akan menarik batas atas untuk Anda secara internal. Sedangkan untuk QT Quick, QML Type FramelessWindow juga akan menarik batas atas.
When running on Wayland, dragging the title bar causes crash? Anda perlu memaksa QT untuk menggunakan XCB QPA saat berjalan di Wayland. Coba atur variabel lingkungan QT_QPA_PLATFORM (sensitif case) ke xcb (sensitif case) sebelum instantiasi setiap contoh Q(Gui)Application . Atau cukup panggil void FramelessHelper::Widgets/Quick::initialize() Dalam fungsi main Anda, fungsi ini akan mengurusnya untuk Anda.
I can see the black background during window resizing?Pertama -tama, ini masalah QT, bukan disebabkan oleh framelesshelper. Dan tidak mungkin untuk aplikasi widget QT. Ini adalah masalah umum untuk aplikasi cepat QT. Sebagian besar waktu yang disebabkan oleh D3D11/Vulkan/Metal karena mereka tidak pandai berurusan dengan operasi pengubah ulang tekstur. Jika Anda benar -benar ingin memperbaiki masalah ini, Anda dapat mencoba mengubah backend RHI QT ke OpenGL (hati -hati dengan bug driver kartu grafis Anda) atau perangkat lunak (jika Anda tidak peduli dengan kinerja). Dan harap diingat bahwa masalah ini tidak dapat diperbaiki dari luar QT.
Can I preserve the window frame border even on Win7? How does Google Chrome/Microsoft Edge's installer achieve that? Jawaban singkat: Ini tidak mungkin. Penjelasan Lengkap: Tentu saja kita dapat menggunakan teknik yang sama yang kita gunakan pada WIN10 untuk menghapus seluruh bagian atas jendela dan melestarikan tiga batas bingkai lainnya pada saat yang sama, tetapi pada WIN10 kita dapat membawa perbatasan atas, baik dengan melakukan beberapa sihir hitam di pawang WM_PAINT atau menggambar perbatasan kerangka tipis sendiri secara manual, namun, tidak mungkin untuk melakukan ini di Win7. Saya sudah mencobanya di Win7 dan sayangnya hasilnya adalah trik WM_PAINT tidak akan berhasil pada Win7, dan kami juga tidak dapat menggambar perbatasan bingkai yang terlihat sangat mirip dengan yang asli (persegi panjang semi-transparan, dicampur dengan warna aksen sistem dan konten visual di belakang jendela, juga dengan efek blur yang diterapkan). Tapi tampaknya installer Google Chrome/Microsoft Edge telah mencapai apa yang ingin kami lakukan, bagaimana? Nah, penginstal mereka adalah open source dan saya sudah membaca kode itu. Mereka mencapainya dengan tumpang tindih dua jendela, satu jendela normal di bagian bawah, jendela tanpa batas lain di bagian atas untuk menutupi bilah judul jendela bawah. Mereka menggambar bilah judul buatan sendiri di jendela tanpa perbatasan dan menggunakannya untuk meniru perilaku bilah judul standar. Bilah judul asli yang disediakan oleh sistem masih ada, tetapi tidak dapat dilihat oleh siapa pun hanya karena ditutupi oleh jendela lain. Saya akui ini adalah solusi yang baik dalam kasus seperti itu tetapi untuk perpustakaan kami itu tidak tepat karena kompleksitas kode akan meledak.
Dipesan dengan waktu kontribusi pertama (mungkin tidak terlalu akurat, maaf)
MIT License
Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.