
QTウィジェットとQTクイック用のクロスプラットフォームウィンドウカスタマイズフレームワーク。 Windows、Linux、MacOSをサポートします。
あなたは私たちの不一致チャンネルに参加して私たちとコミュニケーションをとることができます。より多くのプラットフォームやアプリでFramelesshelper機能の改善 /実装に関する調査結果、考え、アイデアを共有できます!
FramelessDialogクラスを追加しました。 QDialogが一般的なQWidgetよりも優先される場合に使用できます。WindowBorderPainterクラスによって描かれており、また公開されているため、ウィンドウの境界線を簡単に描画する方法を変えることができます。WindowBorder要素が追加されました。クロスプラットフォームの窓境界デコレーターであり、 FramelessHelper要素なしでは機能します。FramelessApplicationWindow要素を追加しました。これは、標準のApplicationWindow要素のシンプルなラッパーであり、タイトルバーを削除してウィンドウの境界を追加するだけです。find_packageを使用してFramelessHelperを検索することが可能になりました。






Qsynthesisフレームワークを使用したVogen Editor。リポジトリ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)を使用して、FramessElperライブラリを見つけてリンクできます。しかし、それを行う前に、 CMAKE_PREFIX_PATHまたはFramelessHelper_DIR変数を渡すことにより、cmakeがframelesshelperを見つける場所を知っていることを確認してください。たとえば、 -DCMAKE_PREFIX_PATH=C:/my-cmake-packages;C:/my-toolchain;etc...または-DFramelessHelper_DIR=C:/Projects/FramelessHelper/lib64/cmake/FramelessHelperもちろん、cmakeプロジェクトのサブディレクトリとしてframelesshelperを構築することもサポートされています。サポートされているframelesshelperターゲット名はFramelessHelper::Core 、 FramelessHelper::Widgets and 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
) framesshelperのクイックモジュールの構文強調表示が必要な場合は、 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オブジェクトをインスタンス化できます。この場合、機能を維持するオブジェクトは1つだけで、他のすべてのオブジェクトはそのラッパーになります。しかし、すべてがスムーズかつ通常行くことを確認するために、いずれにしてもそれをするべきではありません。 FramelessWidgetsHelperオブジェクトをインスタンス化する最も簡単な方法は、静的メソッドFramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *)と呼ぶことです。以前にインスタンス化されたオブジェクトのハンドルがある場合は、新しいオブジェクトが見つからない場合は新しいオブジェクトがインスタンス化されます。このメソッドを同じウィジェットに対して複数回呼び出すことは安全です。既にある場合、新しいオブジェクトがある場合、新しいオブジェクトはインスタンス化されません。また、トップレベルのウィジェットが同じである限り、いつ、どこでその関数を呼び出すかは関係ありません。内部で作成されたオブジェクトは、常にトップレベルのウィジェットに育てられます。 FramelessWidgetsHelperオブジェクトのハンドルを取得したら、 void FramelessWidgetsHelper::extendsContentIntoTitleBar()を呼び出して、オペレーティングシステムが提供するデフォルトのタイトルバーを非表示にすることができます。 FramelessWidgetsHelper正しいトップレベルのウィジェットを見つけることができることを確認するには、 FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *)を呼び出す必要があります。フレームレスウィンドウをドラッグ可能にするには、自家製のタイトルバーウィジェットを自分で提供する必要があります。タイトルバーウィジェットは長方形の形である必要はありません。また、ウィンドウの最初の行に配置する必要もありません。 void FramelessWidgetsHelper::setTitleBarWidget(QWidget *)に電話して、 FramelessHelperにタイトルバーウィジェットを知らせます。デフォルトでは、タイトルバー領域のすべてのウィジェットは、FramelessHelperによって傍受されたため、マウスやキーボードのイベントについて責任を負いません。責任ある状態を回復させるには、テストにヒットするために見えるようにする必要があります。 void FramelessWidgetsHelper::setHitTestVisible(QWidget* )に電話してください。もちろん、タイトルバーの内部にないウィジェットでそれを呼ぶことができますが、それは何の効果もありません。 QT自身の制限により、ルートの親がトップレベルのウィジェットである完全な親チェーンがあることを確認する必要があります。 FramelessWidgetsHelperオブジェクトを削除しようとしないでください。ウィジェットを監視および制御している可能性があり、QTは自動的に削除します。メモリリークを心配する必要はありません。
FramelessWidgetとFramelessMainWindowと呼ばれる2つのクラスもあります。これらは、 FramelessWidgetsHelperのシンプルなラッパーであり、 void FramelessWidgetsHelper::extendsContentIntoTitleBar()機能を保存するだけです。代わりにプレーンQWidget絶対に使用できます。
まず第一に、 void FramelessHelper::Widgets::initialize() main呼び出してくださいQ(Gui|Core)Application
int main ( int , char **)
{
FramelessHelper::Widgets::initialize ();
// ...
}次に、OSが提供する標準タイトルバーを非表示にします。
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ウィンドウジオメトリ/フラグ/状態の変更など、ウィンドウのカスタマイズプロセスを完了した場合にのみ使用できます。この場合、 FramelessHelperのpublic void ready()信号に接続して、正確な時点を取得し、その後休憩の初期化プロセスを実行できます。
まず第一に、非常に早い段階でmain関数のvoid FramelessHelper::Quick::initialize()を呼び出す必要がありますQ(Gui|Core)Applicationオブジェクトの構築前に):
int main ( int , char **)
{
FramelessHelper::Quick::initialize ();
// ...
}次に、QMLエンジンがQMLドキュメントをロードする前に、 void FramelessHelper::Quick::registerTypes(QQmlEngine *)を呼び出すことにより、 FramelessHelperが提供するカスタムタイプを登録する必要があります。
int main ( int , char **)
{
// ...
QQmlApplicationEngine engine;
FramelessHelper::Quick::registerTypes (&engine);
// ...
}これで、QMLドキュメントを書くことができます。 uri org.wangwenx190.FramelessHelperから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に1回だけインスタンス化されます。ただし、 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
}
}理論的には、同じWindowの複数のFramelessHelperオブジェクトをインスタンス化することが可能です。この場合、そのうちの1つだけが機能を維持します。他のすべてのオブジェクトはそれのラッパーになりますが、そうすることは推奨されず、予期しない動作やバグを引き起こす可能性があります。
FramelessHelper関数のいずれかが呼び出し後に効果がないことがわかった場合、最も可能な理由は、関数を呼び出す/ FramelessHelperのプロパティを変更する頃には、ルートウィンドウが初期化プロセスが終了していないため、 FramelessHelperハンドルを取得できないため、ユーザーからのアクションはルートウィンドウが終了するまで無視されます。
また、 FramelessWindowと呼ばれるQMLタイプもあります。これは、 FramelessHelperの単純なラッパーであり、代わりにプレーンWindow絶対に使用できます。
QTクイックアプリケーションの重要な注意:一部の機能は、 FramelessHelperウィンドウジオメトリ/フラグ/状態の変更などのウィンドウのカスタマイズプロセスを完了した場合にのみ利用可能です。この場合、 FramelessHelperのpublic void ready()信号に接続して、正確な時点を取得し、その後の休憩プロセスを実行できます。
Window {
FramelessHelper . onReady : {
// do something here ...
}
} Window {
FramelessHelper {
onReady : {
// do something here ...
}
}
}より詳細な使用法を確認するには、デモプロジェクトを参照してください:例
いくつかの非常にまれなケース(Windows 7でのみ可能)でDWMの組成が無効になっている場合、左上のコーナーと右上コーナーが丸い形で表示されます。丸い角は、DWM組成を再度に可能な場合は四角に復元できます。
いくつかのフレームレスウィンドウには、自家製のタイトルバーの上に奇妙な黒いバーがあり、窓のコントロールがいくつかのピクセルのボトム右コーナーにシフトします。これは、グラフィックカードドライバー、特にframelesshelperではなくOpenGLドライバーのバグです。ユーザーが提供するソリューションはいくつかありますが、それらのいくつかはすべての条件で動作しない場合があります。
| 解決 | 原理 |
|---|---|
| グラフィックドライバーをアップグレードします | 修正で出荷される可能性のある新しいドライバーを使用してみてください |
| システムテーマを「基本」に変更します(「Windows Aero」とは対照的に) | Windowsに純粋なソフトウェアレンダリングを使用させます |
| 複数のグラフィックカードがある場合は、代わりに別のグラフィックカードを使用します | そのようなバグがまったくないかもしれない別のドライバーを使用してみてください |
| システムを少なくともWindows 11にアップグレードします | Windows 11がウィンドウシステムを再設計して、バグをトリガーできなくなりました |
WS_THICKFRAMEとWS_OVERLAPPEDスタイルをウィンドウから削除し、 WS_POPUPスタイルを同時に追加して、 WM_NCCALCSIZEブロック内で何もしないでください(直接falseを返すか、ブロック全体を削除/コメントします) | QTのFramelessWindowHintの動作をミラーリングしてみてください |
WM_NCCALCSIZEトリックを行う代わりに、 Qt::FramelessWindowHintを使用します | QTのレンダリングコードパスは、これら2つのソリューション間でまったく異なります |
| QTにデスクトップopenglの代わりにアングルバックエンドを使用するように強制します | Angleは、OpenGLディレクティブをD3Dのディレクティブに変換します |
| openglを介してレンダリングする代わりに純粋なソフトウェアレンダリングを使用するようQTを強制します | QTはOpenGLをまったく使用していません |
| QTに通常のOpenGLの代わりにMESA 3Dライブラリを使用するように強制します | 別のOpenGL実装を使用してみてください |
| OpenGLの代わりにDirect3D/Vulkan/Metalを使用します | Buggy OpenGLを使用しないでください |
あなたが幸運であれば、そのうちの1人があなたのために問題を修正するかもしれません。そうでない場合は、複数のソリューションを一緒に使用しようとすることができます。しかし、問題が100%修正できることを保証することはできません。
Windows 10には多くのサブバージョンがあるため、Windows 10の最新バージョンを使用することを強くお勧めします。このフレームワークを1507や1607などの非常に古いWindows 10バージョンで使用しようとすると、互換性の問題がいくつかある可能性があります。 Windows 7でこのフレームワークを使用することもサポートされていますが、推奨されません。最も安定した動作と最高の外観を得るには、Windows 10またはWindows 11の最新バージョンで使用する必要があります。
予想通り、スナップレイアウトを機能させるには、自家製システムボタンに次のことを追加するルールがいくつかあります。
setSystemButton()を呼び出し( qwidgetまたはqquickitemにすることができます)、Framelesshelperに最小化/最大化/閉じるボタンを知らせます。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? Waylandで実行するときにXCB QPAを使用するようQTに強制する必要があります。 Q(Gui)Applicationインスタンスをインスタンス化する前に、環境変数QT_QPA_PLATFORM (case sensitive)をxcb (case sensitive)に設定してみてください。または、 void FramelessHelper::Widgets/Quick::initialize() main関数で呼び出すだけで、この関数はあなたのためにそれを処理します。
I can see the black background during window resizing?まず第一に、それはQTの問題であり、framelesshelperによって引き起こされるのではありません。また、QTウィジェットアプリケーションでは不可能です。 QTクイックアプリケーションの一般的な問題です。ほとんどの場合、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で使用するのと同じ手法を使用してウィンドウの上部全体を削除し、他の3つのフレームの境界を同時に保持できますが、Win10では、 WM_PAINTハンドラーでブラックマジックを実行するか、薄いフレームの境界線を描くことで、Win7でこれを行うことは不可能です。私はすでにWin7で試しましたが、残念なことに、 WM_PAINTトリックはWin7で動作しません。また、元の境界線に非常に似たフレームボーダーを描くこともできません(システムのアクセントの色と視覚的なコンテンツとブレンドされた半透明の長方形と、ぼかし効果が適用されます)。しかし、Google Chrome/Microsoft Edgeのインストーラーが私たちがやりたいことを達成したようです。さて、彼らのインストーラーはオープンソースであり、私はすでにコードを読んでいます。彼らは、2つの窓、下部の1つの通常の窓、上部の別の境界のない窓を重複させることでそれを達成し、下部のウィンドウのタイトルバーを覆います。彼らは自家製のタイトルバーを境界のないウィンドウに描き、それを使用して標準のタイトルバーの動作をエミュレートします。システムが提供する元のタイトルバーはまだそこにありますが、別のウィンドウでカバーされているという理由だけで、誰にも見ることができません。そのような場合はそれが良い解決策であることを認めますが、私たちのライブラリにとっては、コードの複雑さが爆発するため、それは適切ではありません。
最初の貢献時に注文しました(それはあまり正確ではないかもしれません、申し訳ありません)
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.