
Marco de personalización de la ventana multiplataforma para widgets QT y QT rápido. Admite Windows, Linux y MacOS.
Puede unirse a nuestro canal Discord para comunicarse con nosotros. ¡Puede compartir sus hallazgos, pensamientos e ideas para mejorar / implementar funcionalidades sin marco en más plataformas y aplicaciones!
FramelessDialog agregada. Puede usarlo en caso de que se prefiera un QDialog sobre un QWidget general.WindowBorderPainter recientemente introducida, y también está expuesta públicamente, por lo que podrá cambiar la forma en que dibujamos el borde de la ventana fácilmente.WindowBorder agregada. Es un decorador de borde de ventana multiplataforma, y puede funcionar sin el elemento FramelessHelper .FramelessApplicationWindow . Es un envoltorio simple del elemento estándar ApplicationWindow , simplemente elimina la barra de título y agrega el borde de la ventana.find_package para encontrar FramelessHelper.






Editor de vogen utilizando el marco de Qsynthesis . URL del repositorio: https://gitee.com/functioner/qvogenclient.
Hay algunas restricciones adicionales para cada plataforma, consulte la sección Notas de la plataforma a continuación.
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! También puede usar Qt6_DIR o Qt5_DIR para reemplazar 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 ...] Si hay algún error al clonar los submódulos, intente ejecutar git submodule update --init --recursive --remote en el directorio del proyecto, ese comando descargará y actualizará todos los submódulos. Si falla nuevamente, intente ejecutarlo varias veces hasta que finalmente tenga éxito.
Una vez que se realice la compilación y la instalación, podrá usar el comando find_package(FramelessHelper REQUIRED COMPONENTS Core Widgets Quick) para encontrar y vincular a la biblioteca framelesshelper. Pero antes de hacerlo, asegúrese de que Cmake sepa dónde encontrar FramelessHelper, pasando la variable CMAKE_PREFIX_PATH o FramelessHelper_DIR . Por ejemplo: -DCMAKE_PREFIX_PATH=C:/my-cmake-packages;C:/my-toolchain;etc... o -DFramelessHelper_DIR=C:/Projects/FramelessHelper/lib64/cmake/FramelessHelper . Construir FramelessHelper como un subdirectorio de su proyecto CMake, por supuesto, también es compatible. Los nombres de Target de marco nocturnos compatibles son FramelessHelper::Core , FramelessHelper::Widgets y FramelessHelper::Quick . Código de ejemplo:
# 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 necesita el resaltado de sintaxis del módulo rápido de FramelessHelper, configure la variable QML_IMPORT_PATH . Código de ejemplo:
# 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) Para personalizar el marco de la ventana de un QWidget, debe instanciar un objeto FramelessWidgetsHelper y luego adjuntarlo al widget de nivel superior del widget, y luego FramelessWidgetsHelper hará todo lo posible para usted: el marco de la ventana se eliminará automáticamente una vez que se haya conectado al widget de nivel superior con éxito. En teoría, puede instanciar múltiples objetos FramelessWidgetsHelper para un mismo widget, en este caso solo habrá un objeto que mantenga funcional, todos los demás objetos se convertirán en un envoltorio de ese. Pero para asegurarse de que todo salga bien y normalmente, no debe hacerlo en ningún caso. La forma más sencilla de instanciar un objeto FramelessWidgetsHelper es llamar al método estático FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *) . Devolverá el mango del objeto previamente instanciado si lo hay, o instanciará un nuevo objeto si no puede encontrar uno. Es seguro llamar a este método varias veces para un mismo widget, no instanciará ningún objeto nuevo si ya hay uno. Tampoco importa cuándo y dónde llamas esa función siempre que el widget de nivel superior sea el mismo. Los objetos creados internamente siempre se parecen al widget de nivel superior. Una vez que obtenga el mango del objeto FramelessWidgetsHelper , puede llamar a void FramelessWidgetsHelper::extendsContentIntoTitleBar() para dejar que oculte la barra de título predeterminada proporcionada por el sistema operativo. Para asegurarse de que FramelessWidgetsHelper pueda encontrar el widget de nivel superior correcto, debe llamar a la función FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *) en un widget que tiene una cadena principal completa cuyo padre raíz es el widget de nivel superior. Para que la ventana sin marco sea arrastrable, debe proporcionar un widget de barra de título casero, el widget de la barra de título no necesita estar en forma rectangular, tampoco es necesario colocar en la primera fila de la ventana. Llame void FramelessWidgetsHelper::setTitleBarWidget(QWidget *) para que FramelessHelper sepa cuál es su widget de barra de título. Por defecto, todos los widgets en el área de la barra de título no serán responsables ante ningún evento de mouse y teclado debido a que los han sido interceptados por FramelessHelper. Para que recuperen el estado responsable, debe hacerlos visibles para llegar a la prueba. Llame void FramelessWidgetsHelper::setHitTestVisible(QWidget* ) para hacer eso. Por supuesto, puede llamarlo en un widget que no está dentro de la barra de título, aunque no tendrá ningún efecto. Debido a las propias limitaciones de QT, debe asegurarse de que su widget tenga una cadena principal completa cuyo padre raíz sea el widget de nivel superior. Nunca intente eliminar el objeto FramelessWidgetsHelper , aún puede estar monitoreando y controlando su widget, y QT lo eliminará automáticamente. No es necesario preocuparse por las filtraciones de memoria.
También hay dos clases llamadas FramelessWidget y FramelessMainWindow , solo son envoltorios simples de FramelessWidgetsHelper , que solo guarda la llamada del void FramelessWidgetsHelper::extendsContentIntoTitleBar() Función para usted. En su lugar, puede usar absolutamente QWidget .
En primer lugar, llame void FramelessHelper::Widgets::initialize() en su función main en una etapa muy temprana ( debe antes de la construcción de cualquier objeto Q(Gui|Core)Application ):
int main ( int , char **)
{
FramelessHelper::Widgets::initialize ();
// ...
}Luego esconde la barra de título estándar proporcionada por el sistema operativo:
MyWidget::MyWidget (QWidget *parent) : QWidget(parent)
{
// You should do this early enough.
FramelessWidgetsHelper::get ( this )-> extendsContentIntoTitleBar ();
// ...
} Luego, deje que FramelessHelper sepa cuál debería ser la barra de título:
void MyWidget::myFunction ()
{
// ...
FramelessWidgetsHelper::get ( this )-> setTitleBarWidget (m_myTitleBarWidget);
// ...
}Luego haga algunos widgets dentro de su barra de título visible para llegar a la prueba:
void MyWidget::myFunction2 ()
{
// ...
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someSearchBox);
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someButton);
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someMenuItem);
// ...
} Nota importante para los widgets QT Aplicaciones : Algunas funcionalidades solo pueden estar disponibles cuando FramelessHelper ha terminado el proceso de personalización de la ventana, como cambiar la geometría/indicadores/estado de la ventana. En este caso, puede conectarse a la señal void ready() de FramelessHelper para obtener el punto de tiempo preciso y hacer su proceso de inicialización de descanso después.
En primer lugar, debe llamar a void FramelessHelper::Quick::initialize() en su función main en una etapa muy temprana ( debe antes de la construcción de cualquier objeto Q(Gui|Core)Application ):
int main ( int , char **)
{
FramelessHelper::Quick::initialize ();
// ...
} Luego, debe registrar los tipos personalizados proporcionados por FramelessHelper llamando a void FramelessHelper::Quick::registerTypes(QQmlEngine *) , antes de que el motor QML cargue cualquier documento QML:
int main ( int , char **)
{
// ...
QQmlApplicationEngine engine;
FramelessHelper::Quick::registerTypes (&engine);
// ...
} Ahora puede escribir sus documentos QML. Debe importar FramelessHelper del URI org.wangwenx190.FramelessHelper . Debe especificar un número de versión justo después de él si está usando QT5:
import org.wangwenx190.FramelessHelper 1.0 // You can use "auto" or omit the version number in Qt6. Y luego puede usar las propiedades adjuntas del tipo 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);
}
}
} Es lo mismo con la interfaz FramelessWidgetsHelper , el tipo QML FramelessHelper se instanciará solo una vez para cada Window , sin importar cuándo y dónde usa las propiedades adjuntas de él. Sin embargo, debido al diseño especial del tipo FramelessHelper , también puede usarlo como un tipo 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 teoría, es posible instanciar múltiples objetos FramelessHelper para una misma Window , en este caso, solo uno de ellos mantendrá funcional, todos los demás objetos se convertirán en un envoltorio de ello, pero no se recomienda hacerlo y puede causar un comportamiento o errores inesperados, por lo tanto, evite tratar de intentarlo en cualquier caso.
Si encuentra que alguna de las funciones FramelessHelper no tiene ningún efecto después de llamar, la razón más posible es que para cuando llama a la función/cambio la propiedad de FramelessHelper , la ventana raíz no ha terminado su proceso de inicialización y, por lo tanto FramelessHelper no puede obtener el mango de ella, por lo que cualquier acción del usuario será ignorada hasta que la ventana raíz terminó la inicialización.
También hay un tipo QML llamado FramelessWindow , es solo una envoltura simple de FramelessHelper , en su lugar puede usar absolutamente Window simple.
Nota importante para aplicaciones rápidas de QT : algunas funcionalidades solo pueden estar disponibles cuando FramelessHelper ha terminado el proceso de personalización de la ventana, como cambiar la geometría/indicadores/estado de la ventana. En este caso, puede conectarse a la señal void ready() de FramelessHelper para obtener el punto de tiempo preciso y hacer su proceso de inicialización de descanso después:
Window {
FramelessHelper . onReady : {
// do something here ...
}
} Window {
FramelessHelper {
onReady : {
// do something here ...
}
}
}Consulte los proyectos de demostración para ver usos más detallados: ejemplos
Si la composición DWM está deshabilitada en algunos casos muy raros (solo es posible en Windows 7), la esquina superior izquierda y la esquina superior derecha aparecerán en forma redonda. Las esquinas redondas se pueden restaurar al cuadrado si vuelve a habilitar la composición DWM.
Hay un error de controlador OpenGL que causará que algunas ventanas sin marco tengan una extraña barra negra justo encima de su barra de título casera, y también hace que los controles en sus ventanas se desplazaran hacia la esquina inferior derecha para algunos píxeles. Es un error del controlador de su tarjeta gráfica, específicamente, su controlador OpenGL, no FramelessHelper. Nuestros usuarios proporcionan algunas soluciones, pero algunas de ellas pueden no funcionar en todas las condiciones, puede elegir una de ellas:
| Solución | Principio |
|---|---|
| Actualizar el controlador de gráficos | Intente usar un controlador más nuevo que pueda enviarse con la solución |
| Cambie el tema del sistema a "básico" (en contrario a "Windows Aero") | Deje que Windows use el reproducción de software puro |
| Si hay varias tarjetas gráficas, use otra en su lugar | Intente usar un controlador diferente que pueda no tener tal error en absoluto |
| Actualice el sistema a al menos Windows 11 | Windows 11 rediseñó el sistema de ventanas para que el error ya no se pueda activar |
Elimine los estilos WS_THICKFRAME y WS_OVERLAPPED de la ventana, y tal vez también agregue el estilo WS_POPUP al mismo tiempo, y no haga nada dentro del bloque WM_NCCALCSIZE (solo devuelva false directamente o elimine/comente todo el bloque) | Trate de reflejar el comportamiento de Qt FramelessWindowHint |
Use Qt::FramelessWindowHint en lugar de hacer el truco WM_NCCALCSIZE | La ruta del código de renderizado de QT es totalmente diferente entre estas dos soluciones |
| Obligar a Qt a usar el backend de ángulo en lugar del escritorio OpenGL | Angle traducirá las directivas OpenGL a las D3D |
| Obligar a QT a usar la representación de software puro en lugar de renderizar a través de OpenGL | QT no está utilizando OpenGL en absoluto |
| Obligar a Qt a usar las bibliotecas Mesa 3D en lugar de OpenGL normal | Intente usar una implementación diferente de OpenGL |
| Use Direct3D/Vulkan/Metal en lugar de OpenGL | Simplemente no use el buggy OpenGL |
Si tiene la suerte, uno de ellos puede solucionar el problema para usted. Si no, puede intentar usar múltiples soluciones juntas. Pero no puedo garantizar que el problema se pueda solucionar al 100%.
Debido a que hay muchas subversiones de Windows 10, se recomienda usar la última versión de Windows 10, al menos no más antiguo que Windows 10 1809 . Si intenta usar este marco en algunas versiones muy antiguas de Windows 10, como 1507 o 1607, puede haber algunos problemas de compatibilidad. El uso de este marco en Windows 7 también es compatible pero no se recomienda. Para obtener el comportamiento más estable y la mejor apariencia, debe usarlo en la última versión de Windows 10 o Windows 11.
Para que el diseño de Snap funcione como se esperaba, hay algunas reglas adicionales para que siga los botones de su sistema caseros:
setSystemButton() para cada botón (puede ser cualquier QWidget o Qquickitem ) para que sepa FramelessHelper cuál es el botón Minimizar/Maximizar/Cerrar.When running on Win10, it seems the top border is missing? But the demo applications still have it? FramelessHelper oculta la barra de título del sistema eliminando toda la parte superior del marco de la ventana, incluido el borde superior. No hay forma de eliminar solo la barra de título del sistema, pero aún así preserva el borde superior al mismo tiempo, incluso Microsoft tampoco puede hacerlo. La razón exacta es desconocida para los desarrolladores que no son de Microsoft, y no tengo interés en cavar en toda la magia detrás de él. Así que tendrás que dibujar uno manualmente para fingir que el borde superior todavía está allí. Puede recuperar su altura y color a través de las API oficiales de DWM. Consulte la documentación de DwmGetWindowAttribute() y DwmGetColorizationColor() . Las aplicaciones de demostración aún tienen el borde superior porque sus ventanas principales heredan de FramelessWidget o FramelessMainWindow , que atraerá el borde superior para usted internamente. En cuanto a Qt Quick, el tipo QML FramelessWindow también dibujará el borde superior.
When running on Wayland, dragging the title bar causes crash? Debe obligar a QT a usar el XCB QPA cuando se ejecute en Wayland. Intente configurar la variable de entorno QT_QPA_PLATFORM (Case Sensitive) en xcb (Sensitive Case) antes de instanciar cualquier instancia Q(Gui)Application . O simplemente llame void FramelessHelper::Widgets/Quick::initialize() En su función main , esta función se encargará de usted.
I can see the black background during window resizing?En primer lugar, es un problema de QT, no causado por FramelessHelper. Y no debería ser posible para las aplicaciones de Widgets QT. Es un problema común para las aplicaciones rápidas QT. La mayoría de las veces es causada por D3D11/Vulkan/Metal porque no son buenos para tratar con operaciones de cambio de tamaño de textura. Si realmente desea solucionar este problema, puede intentar cambiar el backend de RHI de QT a OpenGL (tenga cuidado con el error del controlador de su tarjeta gráfica) o el software (si no le importa el rendimiento). Y tenga en cuenta que este problema no es solucionable desde fuera del QT.
Can I preserve the window frame border even on Win7? How does Google Chrome/Microsoft Edge's installer achieve that? Respuesta corta: es imposible. Explicación completa: por supuesto, podemos usar la misma técnica que usamos en Win10 para eliminar toda la parte superior de la ventana y preservar las otras tres fronteras de marco al mismo tiempo, pero en Win10 podemos traer el borde superior, ya sea haciendo una magia negra en el manejador WM_PAINT o dibujar un borde delgado en el marco manual, sin embargo, es imposible hacerlo en Win7. Ya lo probé en Win7 y lamentablemente el resultado es que el truco WM_PAINT no funcionará en Win7, y tampoco podemos dibujar un borde de cuadro que se vea muy similar al original (un rectángulo semitransparente, combinado con el color de acento del sistema y el contenido visual detrás de la ventana, también con algún efecto desenfoque aplicado). Pero parece que el instalador de Google Chrome/Microsoft Edge ha logrado lo que queríamos hacer, ¿cómo? Bueno, su instalador es de código abierto y ya he leído su código. Lo logran superponiendo dos ventanas, una ventana normal en la parte inferior, otra ventana sin borde en la parte superior para cubrir la barra de título de la ventana inferior. Dibujan su bar de título casero en la ventana sin borde y la usan para emular el comportamiento de la barra de título estándar. La barra de título original proporcionada por el sistema todavía está allí, pero no puede ser visto por nadie solo porque está cubierta por otra ventana. Admito que es una buena solución en tales casos, pero para nuestra biblioteca no es apropiada porque la complejidad del código explotará.
Pedido por el primer tiempo de contribución (puede que no sea muy preciso, lo siento)
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.