
Framework de personnalisation de la fenêtre multiplateforme pour les widgets QT et QT Quick. Prend en charge Windows, Linux et MacOS.
Vous pouvez rejoindre notre canal Discord pour communiquer avec nous. Vous pouvez partager vos résultats, vos pensées et vos idées sur l'amélioration / la mise en œuvre des fonctionnalités sans cadre de FrameHelper sur plus de plateformes et d'applications!
FramelessDialog . Vous pouvez l'utiliser au cas où un QDialog est préféré à un QWidget général.WindowBorderPainter nouvellement introduite, et elle est également exposée publiquement, vous pourrez donc changer facilement la façon dont nous dessinons la bordure de la fenêtre.WindowBorder ajouté. C'est un décorateur de frontières de fenêtre multiplateforme et peut fonctionner sans l'élément FramelessHelper .FramelessApplicationWindow . C'est un simple emballage de l'élément standard ApplicationWindow , supprime simplement la barre de titre et ajoute la bordure de la fenêtre.find_package pour trouver Framelesshelper.






Vogen Editor utilisant le cadre QSynthesis . URL du référentiel: https://gitee.com/functioner/qvogeClient.
Il existe des restrictions supplémentaires pour chaque plate-forme, veuillez vous référer à la section des notes de plate-forme ci-dessous.
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! Vous pouvez également utiliser Qt6_DIR ou Qt5_DIR pour remplacer 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 ...] S'il y a des erreurs lors du clonage des sous-modules, essayez d'exécuter git submodule update --init --recursive --remote Dans le répertoire du projet, cette commande téléchargera et mettra à jour tous les sous-modules. S'il échoue à nouveau, essayez de l'exécuter plusieurs fois jusqu'à ce qu'il réussit enfin.
Une fois la compilation et l'installation terminées, vous pourrez utiliser la commande find_package(FramelessHelper REQUIRED COMPONENTS Core Widgets Quick) pour trouver et lier la bibliothèque sans cadre. Mais avant de le faire, assurez-vous que Cmake sait où trouver Framelesshelper, en passant la variable CMAKE_PREFIX_PATH ou FramelessHelper_DIR . Par exemple: -DCMAKE_PREFIX_PATH=C:/my-cmake-packages;C:/my-toolchain;etc... ou -DFramelessHelper_DIR=C:/Projects/FramelessHelper/lib64/cmake/FramelessHelper . Construisez Framelesshelper en tant que sous-directeur de votre projet CMake est bien sûr également soutenu. Les noms cibles prises en charge sans cadre sont FramelessHelper::Core , FramelessHelper::Widgets et FramelessHelper::Quick . Exemple de code:
# 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
) Si vous avez besoin de la mise en évidence de la syntaxe du module rapide de Framelesshelper, veuillez configurer la variable QML_IMPORT_PATH . Exemple de code:
# 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) Pour personnaliser le cadre de la fenêtre d'un QWidget, vous devez instancier un objet FramelessWidgetsHelper , puis le fixer au widget de niveau supérieur du widget, puis FramelessWidgetsHelper fera tout le travail pour vous: le cadre de fenêtre sera supprimé automatiquement une fois qu'il a été attaché au widget de niveau supérieur avec succès. En théorie, vous pouvez instancier plusieurs objets FramelessWidgetsHelper pour un même widget, dans ce cas, il n'y aura qu'un seul objet qui restera fonctionnel, tous les autres objets deviendront un wrapper de celui-ci. Mais pour vous assurer que tout se passe bien et normalement, vous ne devriez pas le faire en aucun cas. Le moyen le plus simple d'instancier un objet FramelessWidgetsHelper est d'appeler la méthode statique FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *) . Il renverra la poignée de l'objet précédemment instancié le cas échéant, ou il instanciera un nouvel objet s'il n'en trouvera pas. Il est prudent d'appeler cette méthode plusieurs fois pour un même widget, il n'instanacer pas de nouveaux objets s'il y en a déjà un. Peu importe quand et où vous appelez cette fonction tant que le widget de niveau supérieur est le même. Les objets créés en interne seront toujours parents sur le widget de niveau supérieur. Une fois que vous avez obtenu la poignée de l'objet FramelessWidgetsHelper , vous pouvez appeler void FramelessWidgetsHelper::extendsContentIntoTitleBar() pour le laisser masquer la barre de titre par défaut fournie par le système d'exploitation. Afin de vous assurer que FramelessWidgetsHelper peut trouver le widget de niveau supérieur correct, vous devez appeler la fonction FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *) sur un widget qui a une chaîne parent complète dont le parent racine est le widget supérieur. Pour rendre la fenêtre sans cadre dragable, vous devriez fournir un widget de barre de titre fait maison vous-même, le widget de barre de titre n'a pas besoin d'être en forme rectangulaire, il n'a pas non plus besoin d'être placé sur la première ligne de la fenêtre. Appelez void FramelessWidgetsHelper::setTitleBarWidget(QWidget *) pour faire savoir à FramelessHelper quel est votre widget de barre de titre. Par défaut, tous les widgets de la zone de barre de titre ne seront responsables des événements de souris et de clavier en raison de leur interception par Framelesshelper. Pour leur faire récupérer l'état responsable, vous devez les rendre visibles pour avoir un test. Appelez void FramelessWidgetsHelper::setHitTestVisible(QWidget* ) pour le faire. Vous pouvez bien sûr l'appeler sur un widget qui n'est pas du tout à l'intérieur de la barre de titre, cela n'aura aucun effet. En raison des propres limites de QT, vous devez vous assurer que votre widget a une chaîne parentale complète dont le parent racine est le widget de niveau supérieur. N'essayez jamais de supprimer l'objet FramelessWidgetsHelper , il peut toujours surveiller et contrôler votre widget, et QT le supprimera automatiquement. Pas besoin de s'inquiéter des fuites de mémoire.
Il y a aussi deux classes appelées FramelessWidget et FramelessMainWindow , ce ne sont que de simples emballages de FramelessWidgetsHelper , ce qui sauve l'appel de la fonction void FramelessWidgetsHelper::extendsContentIntoTitleBar() pour vous. Vous pouvez absolument utiliser Plain QWidget .
Tout d'abord, appelez void FramelessHelper::Widgets::initialize() dans votre fonction main à un stade très précoce ( doit avant la construction de tout objet Q(Gui|Core)Application ):
int main ( int , char **)
{
FramelessHelper::Widgets::initialize ();
// ...
}Puis cachez la barre de titre standard fournie par le système d'exploitation:
MyWidget::MyWidget (QWidget *parent) : QWidget(parent)
{
// You should do this early enough.
FramelessWidgetsHelper::get ( this )-> extendsContentIntoTitleBar ();
// ...
} Ensuite, laissez FramelessHelper savoir ce qui devrait être la barre de titre:
void MyWidget::myFunction ()
{
// ...
FramelessWidgetsHelper::get ( this )-> setTitleBarWidget (m_myTitleBarWidget);
// ...
}Ensuite, faites des widgets à l'intérieur de votre barre de titre visibles pour frapper le test:
void MyWidget::myFunction2 ()
{
// ...
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someSearchBox);
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someButton);
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someMenuItem);
// ...
} Remarque importante pour les applications de widgets QT : Certaines fonctionnalités ne peuvent être disponibles que lorsque FramelessHelper a terminé le processus de personnalisation de la fenêtre, tel que le changement de géométrie / drapeaux de la fenêtre / état. Dans ce cas, vous pouvez vous connecter au signal public void ready() de FramelessHelper pour obtenir le point temporel précis et effectuer votre processus d'initialisation de repos par la suite.
Tout d'abord, vous devez appeler void FramelessHelper::Quick::initialize() dans votre fonction main à un stade très précoce ( doit avant la construction de tout objet Q(Gui|Core)Application ):
int main ( int , char **)
{
FramelessHelper::Quick::initialize ();
// ...
} Ensuite, vous devez enregistrer les types personnalisés fournis par FramelessHelper en appelant void FramelessHelper::Quick::registerTypes(QQmlEngine *) , avant que le moteur QML charge les documents QML:
int main ( int , char **)
{
// ...
QQmlApplicationEngine engine;
FramelessHelper::Quick::registerTypes (&engine);
// ...
} Vous pouvez maintenant rédiger vos documents QML. Vous devez importer FramelessHelper de l'uri org.wangwenx190.FramelessHelper . Vous devez spécifier un numéro de version juste après cela si vous utilisez QT5:
import org.wangwenx190.FramelessHelper 1.0 // You can use "auto" or omit the version number in Qt6. Et puis vous pouvez utiliser les propriétés attachées à partir du type QML 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);
}
}
} C'est la même chose avec l'interface FramelessWidgetsHelper , le type QML de type FramelessHelper ne sera instancié qu'une seule fois pour chaque Window , peu importe quand et où vous en utilisez des propriétés attachées. Cependant, en raison de la conception spéciale du type FramelessHelper , vous pouvez également l'utiliser comme un type 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
}
} En théorie, il est possible d'instancier plusieurs objets FramelessHelper pour une même Window , dans ce cas, un seul d'entre eux restera fonctionnel, tous les autres objets deviendront un wrapper, mais cela n'est pas recommandé et peut provoquer un comportement ou des bogues inattendus, alors évitez d'essayer de le faire dans tous les cas.
Si vous trouvez que l'une des fonctions FramelessHelper n'a pas d'effet après l'appel, la raison la plus possible est qu'au moment où vous appelez la fonction / modifie la propriété de FramelessHelper , la fenêtre racine n'a pas terminé son processus d'initialisation et donc FramelessHelper ne peut pas en obtenir la poignée, donc toute action de l'utilisateur sera ignorée jusqu'à ce que l'initialisation de la fenêtre racine ne puisse pas finir.
Il existe également un type QML appelé FramelessWindow , ce n'est qu'un simple emballage de FramelessHelper , vous pouvez absolument utiliser Window simple à la place.
Remarque importante pour les applications rapides QT : certaines fonctionnalités ne peuvent être disponibles que lorsque FramelessHelper a terminé le processus de personnalisation de la fenêtre, tel que le changement de géométrie / drapeaux / indicateurs de fenêtre. Dans ce cas, vous pouvez vous connecter au signal Public void ready() de FramelessHelper pour obtenir le point temporel précis et effectuer votre processus d'initialisation de repos par la suite:
Window {
FramelessHelper . onReady : {
// do something here ...
}
} Window {
FramelessHelper {
onReady : {
// do something here ...
}
}
}Veuillez vous référer aux projets de démonstration pour voir des usages plus détaillés: Exemples
Si la composition DWM est désactivée dans certains cas très rares (uniquement possible sur Windows 7), le coin supérieur gauche et le coin supérieur droit apparaîtront en forme ronde. Les coins ronds peuvent être restaurés pour être carrés si vous réactivez la composition DWM.
Il y a un bug de pilote OpenGL qui provoquera des fenêtres sans cadre qui ont une étrange barre noire juste au-dessus de votre barre de titre maison, et cela fait également passer les commandes de vos fenêtres vers le coin inférieur droit pour certains pixels. C'est un bug de votre pilote de carte graphique, en particulier, votre pilote OpenGL, pas Framelesshelper. Il existe certaines solutions fournies par nos utilisateurs, mais certaines d'entre elles peuvent ne pas fonctionner dans toutes les conditions, vous pouvez en choisir une chez eux:
| Solution | Principe |
|---|---|
| Mettre à niveau le pilote graphique | Essayez d'utiliser un nouveau pilote qui peut être expédié avec le correctif |
| Modifiez le thème du système en "Basic" (contraire à "Windows Aero") | Laissez Windows utiliser le rendu des logiciels purs |
| S'il y a plusieurs cartes graphiques, utilisez un autre à la place | Essayez d'utiliser un autre pilote qui peut ne pas avoir un tel bug du tout |
| Améliorez le système à au moins Windows 11 | Windows 11 a repensé le système de fenêtres afin que le bug ne puisse plus être déclenché |
Supprimez les styles WS_THICKFRAME et WS_OVERLAPPED de la fenêtre, et peut-être aussi ajouter le style WS_POPUP en même temps, et ne faites rien dans le bloc WM_NCCALCSIZE (il suffit de retourner false directement ou de supprimer / commenter l'ensemble du bloc) | Essayez de refléter le comportement de QT FramelessWindowHint |
Utilisez Qt::FramelessWindowHint sans WM_NCCALCSIZE | Le chemin de code de rendu de QT est totalement différent entre ces deux solutions |
| Force QT à utiliser le backend d'angle au lieu de l'OpenGL OpenGL de bureau | L'angle traduira les directives OpenGL en d3d |
| Force QT à utiliser le rendu logiciel pur au lieu de rendu via OpenGL | QT n'utilise pas du tout OpenGL |
| Force QT à utiliser les bibliothèques Mesa 3D au lieu de l'OpenGL normal | Essayez d'utiliser une implémentation OpenGL différente |
| Utilisez Direct3D / Vulkan / Metal au lieu d'OpenGL | N'utilisez pas le buggy opengl |
Si vous avez la chance, l'un d'eux peut résoudre le problème pour vous. Sinon, vous pouvez essayer d'utiliser plusieurs solutions ensemble. Mais je ne peux pas garantir que le problème peut être résolu à 100%.
En raison de nombreuses sous-versions de Windows 10, il est fortement recommandé d'utiliser la dernière version de Windows 10, du moins pas plus ancien que Windows 10 1809 . Si vous essayez d'utiliser ce framework sur certaines vieilles versions Windows 10 très anciennes telles que 1507 ou 1607, il peut y avoir des problèmes de compatibilité. L'utilisation de ce cadre sur Windows 7 est également prise en charge mais non recommandée. Pour obtenir le comportement le plus stable et la meilleure apparence, vous devez l'utiliser sur la dernière version de Windows 10 ou Windows 11.
Pour faire fonctionner la disposition SNAP comme prévu, il existe des règles supplémentaires pour que vos boutons système faits maison suivent:
setSystemButton() pour chaque bouton (il peut être n'importe quel QWidget ou QquickItem ) de faire savoir à Framelesshelper quel est le bouton Minimiser / maximiser / fermer.When running on Win10, it seems the top border is missing? But the demo applications still have it? FramelessHelper cache la barre de titre du système en supprimant toute la partie supérieure du cadre de la fenêtre, y compris la bordure supérieure. Il n'y a aucun moyen de supprimer la barre de titre du système mais de préserver toujours la bordure supérieure en même temps, même Microsoft eux-mêmes ne peut pas faire cela non plus. La raison exacte est inconnue des développeurs non microsoft, et je n'ai aucun intérêt à creuser toute la magie derrière. Vous devrez donc en tirer un manuellement vous-même pour prétendre que la frontière supérieure est toujours là. Vous pouvez récupérer sa hauteur et sa couleur via des API DWM officielles. Veuillez vous référer à la documentation de DwmGetWindowAttribute() et DwmGetColorizationColor() . Les applications de démonstration ont toujours la bordure supérieure car leurs fenêtres principales héritent toutes de FramelessWidget ou FramelessMainWindow , qui tirera la bordure supérieure pour vous en interne. Quant à QT rapide, le type QML FramelessWindow dessinera également la bordure supérieure.
When running on Wayland, dragging the title bar causes crash? Vous devez forcer QT à utiliser le XCB QPA lors de la course sur Wayland. Essayez de définir la variable d'environnement QT_QPA_PLATFORM (Case Sensitive) sur xcb (Case Sensitive) avant d'instanciation de toute instance Q(Gui)Application . Ou simplement appeler void FramelessHelper::Widgets/Quick::initialize() Dans votre fonction main , cette fonction s'en occupera pour vous.
I can see the black background during window resizing?Tout d'abord, c'est un problème de QT, non causé par Framelesshelper. Et il ne devrait pas être possible pour les applications de widgets QT. C'est un problème courant pour les applications rapides QT. La plupart du temps, cela est causé par D3D11 / Vulkan / Metal car ils ne sont pas bons pour gérer les opérations de redimensionnement de la texture. Si vous souhaitez vraiment résoudre ce problème, vous pouvez essayer de modifier le backend RHI de QT en OpenGL (faites attention au bogue de votre pilote de carte graphique) ou au logiciel (si vous ne vous souciez pas des performances). Et gardez à l'esprit que ce problème n'est pas réparable de l'extérieur de QT.
Can I preserve the window frame border even on Win7? How does Google Chrome/Microsoft Edge's installer achieve that? Réponse courte: c'est impossible. Explication complète: bien sûr, nous pouvons utiliser la même technique que nous utilisons sur Win10 pour retirer toute la partie supérieure de la fenêtre et préserver les trois autres frontières de cadre en même temps, mais sur Win10, nous pouvons ramener la bordure supérieure, soit en faisant de la magie noire dans le gestionnaire WM_PAINT ou en dessiner une frontière à cadre mince nous-mêmes, mais il est impossible de le faire sur Win7. Je l'ai déjà essayé sur Win7 et, malheureusement, le résultat est que l'astuce WM_PAINT ne fonctionnera pas sur Win7, et nous ne pouvons pas non plus dessiner une bordure de cadre qui ressemble beaucoup à celle d'origine (un rectangle semi-transparent, mélangé avec la couleur d'accent du système et le contenu visuel derrière la fenêtre, également avec un effet flou appliqué). Mais il semble que l'installateur de Google Chrome / Microsoft Edge ait réalisé ce que nous voulions faire, comment? Eh bien, leur installateur est open source et j'ai déjà lu son code. Ils y parviennent en chevauchant deux fenêtres, une fenêtre normale en bas, une autre fenêtre sans bordure en haut pour couvrir la barre de titre de la fenêtre inférieure. Ils dessinent leur barre de titre fait maison sur la fenêtre sans bordure et l'utilisent pour imiter le comportement de la barre de titre standard. La barre de titre originale fournie par le système est toujours là, mais elle ne peut être vue par personne simplement parce qu'elle est couverte par une autre fenêtre. J'avoue que c'est une bonne solution dans de tels cas, mais pour notre bibliothèque, ce n'est pas approprié car la complexité du code explosera.
Ordonné par le premier temps de contribution (il peut ne pas être très précis, désolé)
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.