
Крестоплатформенная структура настройки окна для виджетов QT и QT Quick. Поддерживает Windows, Linux и MacOS.
Вы можете присоединиться к нашему каналу Discord, чтобы общаться с нами. Вы можете поделиться своими выводами, мыслями и идеями по улучшению / внедрению функциональных возможностей без LearlessHelper на дополнительных платформах и приложениях!
FramelessDialog . Вы можете использовать его в случае предпочтения QDialog над общим QWidget .WindowBorderPainter , а также публично открывается, поэтому вы сможете легко изменить то, как мы нарисуем границу окон.WindowBorder . Это кроссплатформенный декоратор границы окон, и он может работать без элемента FramelessHelper .FramelessApplicationWindow . Это простая обертка стандартного элемента ApplicationWindow , просто удаляет строку заголовка и добавляет границу окна.find_package , чтобы найти FramelessHelper.






Редактор Vogen с использованием структуры Qsyntheesis . URL репозитория: https://gitee.com/functioner/qvogenclient.
Есть некоторые дополнительные ограничения для каждой платформы, пожалуйста, обратитесь к разделу «Примечания к платформе» ниже.
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! Вы также можете использовать Qt6_DIR или Qt5_DIR для замены 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 ...] Если есть какие -либо ошибки при клонировании подмодулей, попробуйте запустить git submodule update --init --recursive --remote в каталоге проекта, эта команда будет загружать и обновлять все подмодулы. Если он снова выйдет из строя, попробуйте выполнить его несколько раз, пока он, наконец, не преуспеет.
После того, как компиляция и установка будут выполнены, вы сможете использовать команду find_package(FramelessHelper REQUIRED COMPONENTS Core Widgets Quick) , чтобы найти и ссылаться на библиотеку FramelessHelper. Но прежде чем делать это, убедитесь, что Cmake знает, где найти безвременную, передав переменную CMAKE_PREFIX_PATH или FramelessHelper_DIR . Например: -DCMAKE_PREFIX_PATH=C:/my-cmake-packages;C:/my-toolchain;etc... или -DFramelessHelper_DIR=C:/Projects/FramelessHelper/lib64/cmake/FramelessHelper . Конечно, также поддерживается Build BellessHelper. В качестве подотратчика вашего проекта Cmake также также поддерживается. Поддерживаемые целевые имена FramelessHelper - это FramelessHelper::Core , FramelessHelper::Widgets и FramelessHelper::Quick . Пример кода:
# 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
) Если вам нужен синтаксис, выделяющий быстрый модуль FramelessHelper, пожалуйста, настройте переменную QML_IMPORT_PATH . Пример кода:
# 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) Чтобы настроить оконную рамку Qwidget, вам необходимо создать экземпляр объекта FramelessWidgetsHelper , а затем прикрепить его к виджету верхнего уровня виджета, а затем FramelessWidgetsHelper выполнит все остальные работы для вас: оконная рамка будет удалена автоматически, как только он будет успешно прикреплен к высшему уровню. Теоретически вы можете создать экземпляр множества объектов FramelessWidgetsHelper для одного и того же виджета, в данном случае будет только один объект, который сохраняет функциональные, все другие объекты станут оберткой этого. Но чтобы убедиться, что все идет гладко и обычно, вы не должны делать это в любом случае. Самый простой способ создания объекта FramelessWidgetsHelper - это вызов статического метода FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *) . Он вернет ручку ранее созданного объекта, если таковой имеется, или создаст экземпляр нового объекта, если он не сможет его найти. Можно позвонить в этот метод несколько раз для одного и того же виджета, он не будет создавать каких -либо новых объектов, если он уже есть. Также не имеет значения, когда и где вы называете эту функцию до тех пор, пока виджет верхнего уровня одинаково. Внутренне созданные объекты всегда будут родиться до виджета верхнего уровня. Как только вы получите ручку объекта FramelessWidgetsHelper , вы можете вызвать void FramelessWidgetsHelper::extendsContentIntoTitleBar() чтобы он скрывал строку заголовка по умолчанию, предоставленную операционной системой. Чтобы убедиться, что FramelessWidgetsHelper может найти правильный виджет верхнего уровня, вам следует вызвать функцию FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *) на виджете, у которой есть полный родительский цепь, чей корневой родитель является виджетом верхнего уровня. Чтобы сделать беспрепятственный окно перетаскивать, вы должны предоставить самостоятельный виджет стержня для самого приготовления титула, виджет стержня заголовка не должен быть в прямоугольной форме, а также не нужно размещать на первом ряду окна. Вызовите void FramelessWidgetsHelper::setTitleBarWidget(QWidget *) чтобы сообщить FramelessHelper знать, какой у вас виджет Bar. По умолчанию все виджеты в области строки заголовка не будут нести ответственность за любые события мыши и клавиатуры из -за того, что они были перехвачены FramelessHelper. Чтобы они восстановили ответственное состояние, вы должны сделать их видимыми, чтобы пройти тест. Вызовите void FramelessWidgetsHelper::setHitTestVisible(QWidget* ) чтобы сделать это. Вы, конечно, можете назвать это виджетом, который вообще не находится внутри строки заголовка, хотя он не будет иметь никакого эффекта. Из-за собственных ограничений QT вам необходимо убедиться, что у вашего виджета есть полный родительский цепь, чей основной родитель-виджет верхнего уровня. Никогда не пытайтесь удалить объект FramelessWidgetsHelper , он все еще может контролировать и управлять вашим виджетом, и QT автоматически удалит его для вас. Не нужно беспокоиться о утечках памяти.
Есть также два класса под названием FramelessWidget и FramelessMainWindow , они являются только простыми обертками FramelessWidgetsHelper , который просто сохраняет вызов void FramelessWidgetsHelper::extendsContentIntoTitleBar() для вас. Вместо этого вы можете использовать простой QWidget .
Прежде всего, вызовите void FramelessHelper::Widgets::initialize() в вашей main функции на очень ранней стадии ( необходимо до создания любых объектов Q(Gui|Core)Application :
int main ( int , char **)
{
FramelessHelper::Widgets::initialize ();
// ...
}Затем скрыть стандартную строку заголовка, предоставленная ОС:
MyWidget::MyWidget (QWidget *parent) : QWidget(parent)
{
// You should do this early enough.
FramelessWidgetsHelper::get ( this )-> extendsContentIntoTitleBar ();
// ...
} Затем сообщите, что должно быть в панели названия: FramelessHelper
void MyWidget::myFunction ()
{
// ...
FramelessWidgetsHelper::get ( this )-> setTitleBarWidget (m_myTitleBarWidget);
// ...
}Затем сделайте несколько виджетов внутри вашей панели титула видимым для достижения теста:
void MyWidget::myFunction2 ()
{
// ...
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someSearchBox);
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someButton);
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someMenuItem);
// ...
} Важное примечание для приложений QT Виджетов : Некоторые функциональные возможности могут быть доступны только тогда, когда FramelessHelper завершил процесс настройки окна, такую как изменение геометрии окна/флаги/состояние. В этом случае вы можете подключиться к публичному void ready() сигналу FramelessHelper , чтобы получить точный момент времени, а затем выполнить процесс инициализации покоя.
Прежде всего, вам следует назвать void FramelessHelper::Quick::initialize() в своей main функции на очень ранней стадии ( необходимо до создания любых объектов Q(Gui|Core)Application :
int main ( int , char **)
{
FramelessHelper::Quick::initialize ();
// ...
} Затем вам необходимо зарегистрировать пользовательские типы, предоставленные FramelessHelper , позвонив void FramelessHelper::Quick::registerTypes(QQmlEngine *) , прежде чем двигатель QML загрузит любые документы QML:
int main ( int , char **)
{
// ...
QQmlApplicationEngine engine;
FramelessHelper::Quick::registerTypes (&engine);
// ...
} Теперь вы можете написать свои документы QML. Вы должны импортировать FramelessHelper из URI org.wangwenx190.FramelessHelper . Вы должны указать номер версии сразу после него, если вы используете QT5:
import org.wangwenx190.FramelessHelper 1.0 // You can use "auto" or omit the version number in Qt6. И тогда вы можете использовать прикрепленные свойства из типа 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);
}
}
} То же самое и с интерфейсом FramelessWidgetsHelper , тип QML FramelessHelper будет создаваться только один раз для каждого Window , независимо от того, когда и где вы используете прикрепленные свойства из него. Тем не менее, из -за специальной конструкции типа FramelessHelper , вы также можете использовать его, как обычный тип QML:
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
}
} Теоретически можно создать создание нескольких объектов FramelessHelper для одного и того же Window , в данном случае только один из них будет сохранять функциональные, все другие объекты станут его оберткой, но это не рекомендуется и может вызвать неожиданное поведение или ошибки, поэтому, пожалуйста, избегайте попыток делать это в любом случае.
Если вы обнаружите, что какая -либо из FramelessHelper не имеет эффекта после вызова, наиболее возможная причина - к тому времени, когда вы называете функцию/изменить свойство FramelessHelper , корневое окно не завершило процесс инициализации, и, таким образом, FramelessHelper не может получить его ручку, поэтому любое действие от пользователя будет проигнорировано до тех пор, пока корневое окно завершит инициализацию.
Есть также тип QML под названием FramelessWindow , это всего лишь простая обертка без FramelessHelper , вы можете вместо этого использовать простое Window .
Важное примечание для QT Quick Applications : Некоторые функциональные возможности могут быть доступны только тогда, когда FramelessHelper завершил процесс настройки окна, такую как изменение геометрии окна/флаги/состояние. В этом случае вы можете подключиться к публичному void ready() сигналу FramelessHelper , чтобы получить точный момент времени, а затем выполнить процесс инициализации отдыха:
Window {
FramelessHelper . onReady : {
// do something here ...
}
} Window {
FramelessHelper {
onReady : {
// do something here ...
}
}
}Пожалуйста, обратитесь к демонстрационным проектам, чтобы увидеть более подробные использование: примеры
Если композиция DWM отключена в некоторых очень редких случаях (возможно только в Windows 7), верхний левый угол и правый верхний угол появятся в круглой форме. Круглые углы могут быть восстановлены в квадрате, если вы повторно устанавливаете композицию DWM.
Есть ошибка драйвера OpenGL, которая приведет к тому, что некоторые безрамные окна имеют странную черную планку прямо на вершине вашей домашней панели заголовка, а также делает элементы управления в ваших окнах, которые перемещаются в правый нижний угол для некоторых пикселей. Это ошибка вашего драйвера для видеокарты, в частности, ваш драйвер OpenGL, а не безвкусно -халпер. Есть некоторые решения, предоставляемые нашими пользователями, но некоторые из них могут не работать в всех условиях, вы можете выбрать один из них:
| Решение | Принцип |
|---|---|
| Обновить графический драйвер | Попробуйте использовать более нового водителя, который может отправиться с исправлением |
| Измените тему системы на «базовый» (в отличие от «Windows Aero») | Пусть Windows использует чистого программного рендеринга |
| Если есть несколько видеокарт, вместо этого используйте еще один | Попробуйте использовать другой драйвер, который может не иметь такой ошибки вообще |
| Обновите систему как минимум на Windows 11 | Windows 11 перепроектировал систему окон, чтобы ошибка больше не была запускается |
Удалите стили WS_THICKFRAME и WS_OVERLAPPED из окна, а также, возможно, также добавьте стиль WS_POPUP одновременно, и не делайте ничего внутри блока WM_NCCALCSIZE (просто верните false или удалите/прокомментируйте весь блок) | Попробуйте отразить поведение QT FramelessWindowHint |
Используйте Qt::FramelessWindowHint вместо того, чтобы выполнять трюк WM_NCCALCSIZE | Путь кода QT полностью отличается между этими двумя решениями |
| Заставьте QT использовать бэкэнд на угле вместо рабочего стола OpenGlgl | Угол будет перевести директивы OpenGL в D3D |
| Заставляйте QT использовать чистый программный рендеринг вместо рендеринга через OpenGL | QT вообще не использует OpenGL |
| Заставлять QT использовать Mesa 3D библиотеки вместо обычного OpenGL | Попробуйте использовать другую реализацию OpenGL |
| Используйте Direct3d/Vulkan/Metal вместо OpenGL | Только не используйте багги Opengl |
Если вам повезло, один из них может решить проблему для вас. Если нет, вы можете попытаться использовать несколько решений вместе. Но я не могу гарантировать, что проблема может быть исправлена на 100%.
Из-за того, что есть много подражоров Windows 10, настоятельно рекомендуется использовать последнюю версию Windows 10, по крайней мере, не старше, чем Windows 10 1809 . Если вы попытаетесь использовать эту структуру в некоторых очень старых версиях Windows 10, таких как 1507 или 1607, могут возникнуть некоторые проблемы с совместимостью. Использование этой структуры в Windows 7 также поддерживается, но не рекомендуется. Чтобы получить наиболее стабильное поведение и лучший внешний вид, вы должны использовать его на последней версии Windows 10 или Windows 11.
Чтобы сделать макет SNAP, как и ожидалось, существуют некоторые дополнительные правила для ваших домашних системных кнопок:
setSystemButton() для каждой кнопки (это может быть любой qwidget или qquickitem ), чтобы Learlesshelper знал, какая кнопка минимиза/максимиза/закрыть.When running on Win10, it seems the top border is missing? But the demo applications still have it? FramelessHelper скрывает строку заголовка системы, удаляя всю верхнюю часть оконной рамы, включая верхнюю границу. Там нет никакого способа удалить только строку заголовка системы, но все же сохранить верхнюю границу одновременно, даже Microsoft не может этого сделать. Точная причина неизвестна не-микрософт-разработчикам, и я не заинтересован в том, чтобы копаться во всех магиях. Таким образом, вам придется нарисовать один вручную самостоятельно, чтобы притворяться, что верхняя граница все еще там. Вы можете получить его высоту и цвет через официальные DWM API. Пожалуйста, обратитесь к документации DwmGetWindowAttribute() и DwmGetColorizationColor() . Демо -приложения по -прежнему имеют верхнюю границу, потому что их основные окна наследуют от FramelessWidget или FramelessMainWindow , которые будут нарисовать верхнюю границу для вас. Что касается QT Quick, то тип QML FramelessWindow также проведет верхнюю границу.
When running on Wayland, dragging the title bar causes crash? Вам нужно заставить QT использовать XCB QPA при запуске в Уэйленде. Попробуйте установить переменную среды QT_QPA_PLATFORM (case cellessitive) на xcb (Case Cellestive), прежде чем создавать любые экземпляры Q(Gui)Application . Или просто вызовите void FramelessHelper::Widgets/Quick::initialize() В вашей main функции эта функция позаботится о вас.
I can see the black background during window resizing?Прежде всего, это проблема QT, не вызванная беспрепятственной защитой. И это не должно быть возможно для приложений QT Vidgets. Это общая проблема для QT Quick Applications. Большую часть времени это вызвано D3D11/Vulkan/Metal, потому что они не умеют справляться с операциями изменения размера текстур. Если вы действительно хотите решить эту проблему, вы можете попытаться изменить бэкэнд QT RHI на OpenGL (будьте осторожны с ошибкой вашего драйвера видеокарты) или программного обеспечения (если вам не волнует производительность). И, пожалуйста, имейте в виду, что эта проблема не поддается решению извне QT.
Can I preserve the window frame border even on Win7? How does Google Chrome/Microsoft Edge's installer achieve that? Короткий ответ: это невозможно. Полное объяснение: конечно, мы можем использовать ту же технику, которую мы используем на Win10, чтобы удалить всю верхнюю часть окна и одновременно сохранить три других кадрских границ, но на Win10 мы можем вернуть верхнюю границу, либо сделав какую -то черную магию в обработчике WM_PAINT , либо привлечь к себе границу с тонкой кадром, однако, невозможно сделать это на Win7. Я уже пробовал это на Win7, и, к сожалению, результат в том, что трюк WM_PAINT не будет работать на Win7, и мы также не можем нарисовать границу кадра, которая выглядит очень похоже на оригинальный (полупрозрачный прямоугольник, смешанный с акцентным цветом системы и визуальным содержанием за окном, также с некоторым применением размытого эффекта). Но, кажется, Google Chrome/Microsoft Edge Installer достиг того, что мы хотели сделать, как? Ну, их установщик открыт с открытым исходным кодом, и я уже прочитал его код. Они достигают этого, перекрыв два окна, одно обычное окно внизу, еще одно окно без границы сверху, чтобы покрыть строку заголовка нижнего окна. Они рисуют свою домашнюю панель титула в окно без границы и используют его для эмуляции поведения стандартного титула. Оригинальная строка заголовка, предоставленная системой, все еще существует, но никто не видит никого только потому, что она покрыта другим окном. Я признаю, что в таких случаях это хорошее решение, но для нашей библиотеки это не подходит, потому что сложность кода взорвется.
Заказано во время первого взноса (это может быть не очень точным, извините)
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.