
Estrutura de personalização da janela de plataforma cruzada para widgets QT e QT rápido. Suporta Windows, Linux e MacOS.
Você pode se juntar ao nosso canal Discord para se comunicar conosco. Você pode compartilhar suas descobertas, pensamentos e idéias sobre como melhorar / implementar as funcionalidades do FramelessHelper em mais plataformas e aplicativos!
FramelessDialog . Você pode usá -lo caso um QDialog seja preferido em relação a um QWidget geral.WindowBorderPainter e também é exposta publicamente, para que você possa alterar a maneira como desenhamos a fronteira da janela facilmente.WindowBorder . É um decorador de borda da janela de plataforma cruzada e pode funcionar sem o elemento FramelessHelper .FramelessApplicationWindow . É um invólucro simples do elemento ApplicationWindow padrão, basta remover a barra de título e adicionar a borda da janela.find_package para encontrar o FramelessHelper.






Editor de Vogen usando a estrutura Qsynthesis . URL do repositório: https://gitee.com/functioner/qvogenclient.
Existem algumas restrições adicionais para cada plataforma, consulte a seção Notas da plataforma abaixo.
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! Você também pode usar Qt6_DIR ou Qt5_DIR para substituir 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 ...] Se houver algum erro ao clonar os submódulos, tente executar git submodule update --init --recursive --remote no diretório do projeto, esse comando baixará e atualizará todos os submódulos. Se falhar novamente, tente executá -lo várias vezes até que finalmente seja bem -sucedido.
Depois que a compilação e a instalação forem concluídas, você poderá usar o comando find_package(FramelessHelper REQUIRED COMPONENTS Core Widgets Quick) para encontrar e vincular a biblioteca do FramelessHelper. Mas antes de fazer isso, certifique -se de que o Cmake saiba onde encontrar o FramelessHelper, passando a variável CMAKE_PREFIX_PATH ou FramelessHelper_DIR . Por exemplo: -DCMAKE_PREFIX_PATH=C:/my-cmake-packages;C:/my-toolchain;etc... ou -DFramelessHelper_DIR=C:/Projects/FramelessHelper/lib64/cmake/FramelessHelper . Construir o FramelessHelper como um subdiretório do seu projeto CMake também é suportado. Os nomes de alvos do FramelessHelper suportados são FramelessHelper::Core , FramelessHelper::Widgets e FramelessHelper::Quick . Exemplo Código:
# 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
) Se você precisar do destaque da sintaxe do módulo rápido do FramlessHelper, configure a variável QML_IMPORT_PATH . Exemplo Código:
# 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 o quadro da janela de um qwidget, você precisa instanciar um objeto FramelessWidgetsHelper e conectá -lo ao widget de nível superior do widget, e, em seguida, FramelessWidgetsHelper fará todo o funcionamento do restante para você: o quadro da janela será removido automaticamente quando estiver anexado ao nível superior com sucesso. Em teoria, você pode instanciar vários objetos FramelessWidgetsHelper para um mesmo widget; neste caso, haverá apenas um objeto que manterá funcionais, todos os outros objetos se tornarão um invólucro desse. Mas, para garantir que tudo corra bem e normalmente, você não deve fazer isso em nenhum caso. A maneira mais simples de instanciar um objeto FramelessWidgetsHelper é chamar de método estático FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *) . Ele retornará o identificador do objeto anteriormente instanciado, se houver, ou instanciará um novo objeto se não conseguir encontrar um. É seguro chamar esse método várias vezes para o mesmo widget, ele não instanciará novos objetos se já houver um. Também não importa quando e onde você chama isso de função, desde que o widget de nível superior seja o mesmo. Os objetos criados internamente sempre serão pais para o widget de nível superior. Depois de obter o identificador do objeto FramelessWidgetsHelper , você pode chamar void FramelessWidgetsHelper::extendsContentIntoTitleBar() para que oculte a barra de título padrão fornecida pelo sistema operacional. Para garantir que FramelessWidgetsHelper possa encontrar o widget de nível superior correto, você deve ligar para a função de FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *) em um widget que possui uma cadeia parental completa cujo pai raiz é o widget de nível superior. Para tornar a janela sem moldura arrastada, você deve fornecer um widget de barra de título caseiro, o widget da barra de título não precisa estar em forma retangular, ele também não precisa ser colocado na primeira linha da janela. Ligue para void FramelessWidgetsHelper::setTitleBarWidget(QWidget *) para que FramelessHelper saiba qual é o seu widget de barra de título. Por padrão, todos os widgets na área da barra de título não serão responsáveis por qualquer evento de mouse e teclado, devido a que foram interceptados pelo FramelessHelper. Para fazê -los recuperar o estado responsável, você deve torná -los visíveis para acertar o teste. Ligue para void FramelessWidgetsHelper::setHitTestVisible(QWidget* ) para fazer isso. É claro que você pode chamá -lo em um widget que não está dentro da barra de título, mas não terá nenhum efeito. Devido às próprias limitações do QT, você precisa garantir que seu widget tenha uma cadeia pai completa cujo pai raiz é o widget de nível superior. Nunca tente excluir o objeto FramelessWidgetsHelper , ele ainda pode estar monitorando e controlando seu widget e o QT o excluirá automaticamente. Não há necessidade de se preocupar com vazamentos de memória.
Existem também duas classes chamadas FramelessWidget e FramelessMainWindow , elas são apenas invólucros simples de FramelessWidgetsHelper , que apenas salvam o chamado da função void FramelessWidgetsHelper::extendsContentIntoTitleBar() para você. Você pode absolutamente usar QWidget simples.
Primeiro de tudo, ligue para void FramelessHelper::Widgets::initialize() em sua função main em um estágio muito inicial ( deve antes da construção de qualquer Objetos Q(Gui|Core)Application ):
int main ( int , char **)
{
FramelessHelper::Widgets::initialize ();
// ...
}Em seguida, oculte a barra de título padrão fornecida pelo sistema operacional:
MyWidget::MyWidget (QWidget *parent) : QWidget(parent)
{
// You should do this early enough.
FramelessWidgetsHelper::get ( this )-> extendsContentIntoTitleBar ();
// ...
} Em seguida, deixe FramelessHelper saber qual deve ser a barra de título:
void MyWidget::myFunction ()
{
// ...
FramelessWidgetsHelper::get ( this )-> setTitleBarWidget (m_myTitleBarWidget);
// ...
}Em seguida, faça alguns widgets dentro da sua barra de título visíveis para acertar o teste:
void MyWidget::myFunction2 ()
{
// ...
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someSearchBox);
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someButton);
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someMenuItem);
// ...
} Nota importante para aplicativos de widgets QT : Algumas funcionalidades só podem estar disponíveis quando FramelessHelper terminar o processo de personalização da janela, como alteração da geometria da janela/sinalizadores/estado. Nesse caso, você pode se conectar ao sinal void ready() do Sinal de FramelessHelper para obter o ponto de tempo preciso e fazer o seu processo de inicialização de descanso posteriormente.
Primeiro de tudo, você deve chamar void FramelessHelper::Quick::initialize() em sua função main em um estágio muito inicial ( deve antes da construção de qualquer Objetos Q(Gui|Core)Application ):
int main ( int , char **)
{
FramelessHelper::Quick::initialize ();
// ...
} Em seguida, você precisa registrar os tipos personalizados fornecidos pela FramelessHelper chamando void FramelessHelper::Quick::registerTypes(QQmlEngine *) , antes que o mecanismo QML carregue qualquer documento QML:
int main ( int , char **)
{
// ...
QQmlApplicationEngine engine;
FramelessHelper::Quick::registerTypes (&engine);
// ...
} Agora você pode escrever seus documentos QML. Você deve importar FramelessHelper do Uri org.wangwenx190.FramelessHelper . Você deve especificar um número de versão logo após ele se estiver usando o QT5:
import org.wangwenx190.FramelessHelper 1.0 // You can use "auto" or omit the version number in Qt6. E então você pode usar as propriedades anexadas do 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);
}
}
} É o mesmo com a interface FramelessWidgetsHelper , o tipo FramelessHelper do tipo QML será instanciado apenas uma vez para cada Window , não importa quando e onde você usa propriedades anexadas. No entanto, devido ao design especial do tipo de FramelessHelper , você também pode usá -lo como um 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
}
} Em teoria, é possível instanciar múltiplos objetos FramelessHelper para uma mesma Window , neste caso apenas um deles manterá funcional, todos os outros objetos se tornarão um invólucro, mas isso não é recomendado e pode causar comportamento ou bugs inesperados; portanto, evite fazer isso em qualquer caso.
Se você encontrar alguma das funções FramelessHelper não tiver efeito após a chamada, o motivo mais possível é que, quando você chamar a função/alterar a propriedade do FramelessHelper , a janela da raiz não terminou seu processo de inicialização e, portanto, o FramelessHelper não pode obter o identificador, portanto, qualquer ação do usuário será ignorada até a inicialização da janela.
Há também um tipo QML chamado FramelessWindow , é apenas um invólucro simples de FramelessHelper , você pode absolutamente usar Window simples.
NOTA IMPORTANTE PARA APLICAÇÕES RÁPIDAS QT : Algumas funcionalidades só podem estar disponíveis quando FramelessHelper terminar o processo de personalização da janela, como alteração da geometria da janela/sinalizadores/estado. Nesse caso, você pode se conectar ao sinal void ready() do Sinal de FramelessHelper para obter o ponto de tempo preciso e fazer o seu processo de inicialização de descanso posteriormente:
Window {
FramelessHelper . onReady : {
// do something here ...
}
} Window {
FramelessHelper {
onReady : {
// do something here ...
}
}
}Consulte os projetos de demonstração para ver usos mais detalhados: exemplos
Se a composição DWM estiver desativada em alguns casos muito raros (apenas possível no Windows 7), o canto superior esquerdo e o canto superior direito aparecerão em forma redonda. Os cantos redondos podem ser restaurados no quadrado se você reativar a composição DWM.
Há um bug de driver OpenGL que fará com que algumas janelas sem molduras tenham uma barra preta estranha em cima da sua barra de título caseira, e também faz com que os controles nas janelas deslocassem para o canto inferior direito para alguns pixels. É um bug do seu driver de placa de gráfico, especificamente, seu driver OpenGL, não o FramelessHelper. Existem algumas soluções fornecidas por nossos usuários, mas alguns deles podem não funcionar em todas as condições, você pode escolher um deles:
| Solução | Princípio |
|---|---|
| Atualize o driver gráfico | Tente usar um motorista mais novo que possa ser enviado com a correção |
| Altere o tema do sistema para "Basic" (ao contrário de "Windows Aero") | Deixe o Windows usar a renderização de software pura |
| Se houver várias placas gráficas, use outra | Tente usar um driver diferente que pode não ter esse bug |
| Atualize o sistema para pelo menos o Windows 11 | Windows 11 redesenhou o sistema de janelas para que o bug não possa mais ser acionado |
Remova os estilos WS_THICKFRAME e WS_OVERLAPPED da janela e talvez também adicione o estilo WS_POPUP ao mesmo tempo e não faça nada dentro do bloco WM_NCCALCSIZE (basta retornar false diretamente ou remover/comentar o bloco inteiro) | Tente espelhar o comportamento do QT de FramelessWindowHint |
Use Qt::FramelessWindowHint em vez de fazer o truque WM_NCCALCSIZE | O caminho do código de renderização do QT é totalmente diferente entre essas duas soluções |
| Força Qt a usar o back -end do ângulo em vez do desktop openGL | O ângulo traduzirá as diretrizes do OpenGL para o D3D |
| Força QT a usar a renderização de software pura em vez de renderizar através do OpenGL | Qt não está usando o OpenGL |
| Força Qt a usar as bibliotecas Mesa 3D em vez de openGL normal | Tente usar uma implementação OpenGL diferente |
| Use Direct3D/Vulkan/Metal em vez de OpenGL | Só não use o buggy opengl |
Se você tiver sorte o suficiente, um deles pode corrigir o problema para você. Caso contrário, você pode tentar usar várias soluções juntas. Mas não posso garantir que o problema possa ser corrigido 100%.
Devido a muitas subversões do Windows 10, é altamente recomendável usar a versão mais recente do Windows 10, pelo menos não mais antiga que o Windows 10 1809 . Se você tentar usar essa estrutura em algumas versões muito antigas do Windows 10, como 1507 ou 1607, pode haver alguns problemas de compatibilidade. O uso dessa estrutura no Windows 7 também é suportado, mas não recomendado. Para obter o comportamento mais estável e a melhor aparência, você deve usá -lo na versão mais recente do Windows 10 ou Windows 11.
Para fazer o layout do SNAP funcionar como esperado, existem algumas regras adicionais para os botões do sistema caseiro seguirem:
setSystemButton() para cada botão (pode ser qualquer QWIdget ou Qquickitem ) para informar o FramelessHelper que é o botão Minimize/Maximize/Fechar.When running on Win10, it seems the top border is missing? But the demo applications still have it? FramelessHelper oculta a barra de título do sistema, removendo toda a parte superior da estrutura da janela, incluindo a borda superior. Não há como remover apenas a barra de título do sistema, mas ainda preservar a borda superior ao mesmo tempo, mesmo a Microsoft também não pode fazer isso. O motivo exato é desconhecido para os desenvolvedores não-Microsoft, e não tenho interesse em cavar toda a magia por trás disso. Então você terá que desenhar um manualmente para fingir que a borda superior ainda está lá. Você pode recuperar sua altura e cor através de APIs oficiais do DWM. Consulte a documentação de DwmGetWindowAttribute() e DwmGetColorizationColor() . Os aplicativos de demonstração ainda têm a borda superior, porque suas principais janelas herdam da FramelessWidget ou FramelessMainWindow , que desenharão a borda superior para você internamente. Quanto ao QT rápido, o QML Type FramelessWindow também desenhará a borda superior.
When running on Wayland, dragging the title bar causes crash? Você precisa forçar o QT a usar o XCB QPA ao executar em Wayland. Tente definir a variável de ambiente QT_QPA_PLATFORM (case sensível) como xcb (sensível ao caso) antes de instantar qualquer instância Q(Gui)Application . Ou basta ligar para void FramelessHelper::Widgets/Quick::initialize() Na sua função main , essa função cuidará dela para você.
I can see the black background during window resizing?Primeiro de tudo, é uma edição do QT, não causada por FramelessHelper. E não deve ser possível para aplicativos de widgets QT. É um problema comum para aplicativos rápidos do QT. Na maioria das vezes, é causado por D3D11/Vulkan/Metal porque eles não são bons em lidar com operações de redimensionamento de textura. Se você realmente deseja corrigir esse problema, tente alterar o back -end do QT para o OpenGL (tenha cuidado com o bug do seu driver de placa gráfica) ou software (se você não se importa com o desempenho). E lembre -se de que esse problema não é corrigível fora do QT.
Can I preserve the window frame border even on Win7? How does Google Chrome/Microsoft Edge's installer achieve that? Resposta curta: é impossível. Explicação completa: é claro que podemos usar a mesma técnica que usamos no Win10 para remover toda a parte superior da janela e preservar as outras três fronteiras de quadros ao mesmo tempo, mas no Win10 podemos trazer a borda superior de volta, seja fazendo alguma magia negra no WM_PAINT Handler ou desenhar uma borda fina de armação fino manualmente, no entanto, o que é impossível de fazer isso no win7. Já tentei no Win7 e, infelizmente, o resultado é o truque WM_PAINT não funcionará no Win7, e também não podemos desenhar uma borda de quadro que se parece muito com a original (um retângulo semi-transparente, misturado com a cor de destaque do sistema e o conteúdo visual atrás da janela, também com algum efeito de desfoque aplicado). Mas parece que o instalador do Google Chrome/Microsoft Edge alcançou o que queríamos fazer, como? Bem, o instalador deles é de código aberto e já li seu código. Eles conseguem isso sobrepondo duas janelas, uma janela normal na parte inferior, outra janela sem borda na parte superior para cobrir a barra de título da janela inferior. Eles desenham sua barra de título caseira na janela sem fronteira e a usam para imitar o comportamento da barra de título padrão. A barra de título original fornecida pelo sistema ainda está lá, mas não pode ser vista por ninguém apenas porque está coberta por outra janela. Admito que é uma boa solução nesses casos, mas para a nossa biblioteca não é apropriada porque a complexidade do código explodirá.
Ordenado pelo tempo de primeira contribuição (pode não ser muito preciso, desculpe)
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.