
Plattform-Fensteranpassungs-Framework für QT-Widgets und QT Quick. Unterstützt Windows, Linux und MacOS.
Sie können sich unserem Discord -Kanal anschließen, um mit uns zu kommunizieren. Sie können Ihre Erkenntnisse, Gedanken und Ideen zur Verbesserung / Implementierung von FramelessHelper -Funktionen auf weiteren Plattformen und Apps teilen!
FramelessDialog -Klasse hinzugefügt. Sie können es verwenden, falls ein QDialog gegenüber einem allgemeinen QWidget bevorzugt wird.WindowBorderPainter -Klasse gezeichnet und sind auch öffentlich aufgedeckt, sodass Sie so leicht ändern können, wie wir den Fensterrand problemlos zeichnen.WindowBorder Element hinzugefügt. Es ist ein plattformübergreifender Fenster-Grenzdekorator und kann ohne das Element FramelessHelper arbeiten.FramelessApplicationWindow hinzugefügt. Es ist ein einfacher Wrapper des Standard ApplicationWindow -Elements, entfernt nur die Titelleiste und fügt den Fensterrand hinzu.find_package zu verwenden, um FramelessHelper zu finden.






Vogen -Editor unter Verwendung des Qsynthesis -Frameworks. Repository -URL: https://gitee.com/functioner/qvogenclient.
Für jede Plattform gibt es einige zusätzliche Einschränkungen. Weitere Informationen finden Sie im Abschnitt " Plattform" unten.
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! Sie können auch Qt6_DIR oder Qt5_DIR verwenden, um CMAKE_PREFIX_PATH zu ersetzen:
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 ...] Wenn beim Klonen der Submodules Fehler vorhanden sind, versuchen Sie, git submodule update --init --recursive --remote im Projektverzeichnis auszuführen, dieser Befehl wird alle Submodules herunterladen und aktualisieren. Wenn es erneut fehlschlägt, versuchen Sie es mehrmals auszuführen, bis es schließlich erfolgreich ist.
Sobald die Zusammenstellung und Installation durchgeführt ist, können Sie den Befehl find_package(FramelessHelper REQUIRED COMPONENTS Core Widgets Quick) verwenden, um die FramelessHelper -Bibliothek zu finden und zu verlinken. Bevor Sie dies jedoch tun, stellen Sie bitte sicher, dass CMAKE weiß, wo er FramelessHelper findet, indem Sie die Variable CMAKE_PREFIX_PATH oder FramelessHelper_DIR dazu übergeben. Zum Beispiel: -DCMAKE_PREFIX_PATH=C:/my-cmake-packages;C:/my-toolchain;etc... -DFramelessHelper_DIR=C:/Projects/FramelessHelper/lib64/cmake/FramelessHelper BAME FRAMELESLEHELPER als Unterabschluss Ihres CMAKE-Projekts wird natürlich auch unterstützt. Die unterstützten FramelessHelper -Zielnamen sind FramelessHelper::Core , FramelessHelper::Widgets und FramelessHelper::Quick . Beispielcode:
# 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
) Wenn Sie das Schnellmodul von FramelessHelper von Syntax benötigen, richten Sie bitte die Variable QML_IMPORT_PATH ein. Beispielcode:
# 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) Um den Fensterrahmen eines Qwidgets anzupassen, müssen Sie ein FramelessWidgetsHelper -Objekt instanziieren und anschließend an das Widget des Widget -Widgets angeschlossen werden. Anschließend erledigt FramelessWidgetsHelper das gesamte Ruhestand für Sie: Der Fensterrahmen wird automatisch entfernt. Theoretisch können Sie mehrere FramelessWidgetsHelper -Objekte für das gleiche Widget instanziieren. In diesem Fall gibt es nur ein Objekt, das funktionsfähig bleibt. Alle anderen Objekte werden zu einem Wrapper davon. Um sicherzustellen, dass alles reibungslos und normalerweise läuft, sollten Sie dies auf keinen Fall tun. Der einfachste Weg, um ein FramelessWidgetsHelper -Objekt zu instanziieren, besteht darin, die statische Methode FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *) aufzurufen. Es wird das Handle des zuvor instanziierten Objekts zurückgegeben, falls vorhanden, oder es wird ein neues Objekt instanziiert, wenn es keine finden kann. Es ist sicher, diese Methode mehrmals für dasselbe Widget aufzurufen. Es wird keine neuen Objekte instanziiert, wenn es bereits eine gibt. Es spielt auch keine Rolle, wann und wo Sie diese Funktion nennen, solange das Widget der obersten Ebene gleich ist. Die intern erstellten Objekte werden immer dem Widget der obersten Ebene verlagert. Sobald Sie das Handle des FramelessWidgetsHelper -Objekts erhalten haben, können Sie void FramelessWidgetsHelper::extendsContentIntoTitleBar() nennen, um die vom Betriebssystem bereitgestellte Standardtitelleiste auszublenden. Um sicherzustellen, dass FramelessWidgetsHelper das richtige Widget der obersten Ebene finden kann, sollten Sie die FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *) -Funktion auf einem Widget mit einer vollständigen übergeordneten Kette des übergeordneten übergeordneten übergeordneten Widgets auf der obersten Ebene aufrufen. Um das rahmenlose Fenster draggierbar zu machen, sollten Sie selbst ein hausgemachtes Titelbalken -Widget anbieten. Das Titelbalken -Widget muss nicht in rechteckiger Form sein, es muss auch nicht in der ersten Zeile des Fensters platziert werden. Rufen Sie void FramelessWidgetsHelper::setTitleBarWidget(QWidget *) FramelessHelper Standardmäßig sind alle Widgets im Titelbalkenbereich für keine Maus- und Tastaturereignisse verantwortlich, die von FramelessHelper abgefangen wurden. Damit sie den verantwortlichen Zustand wiederherstellen, sollten Sie sie sichtbar machen, um die Prüfung zu treffen. Rufen Sie void FramelessWidgetsHelper::setHitTestVisible(QWidget* ) an, um dies zu tun. Sie können es natürlich auf einem Widget anrufen, das überhaupt nicht in der Titelleiste liegt, es hat jedoch keinen Effekt. Aufgrund der eigenen Einschränkungen von QT müssen Sie sicherstellen, dass Ihr Widget über eine vollständige Elternkette verfügt, deren Root-Eltern das Widget der obersten Ebene ist. Versuchen Sie niemals, das FramelessWidgetsHelper -Objekt zu löschen, es kann weiterhin Ihr Widget überwachen und steuern, und QT löscht es automatisch für Sie. Sie müssen sich keine Sorgen um Speicherlecks machen.
Es gibt auch zwei Klassen, die als FramelessWidget und FramelessMainWindow bezeichnet werden. Sie sind nur einfache Wrapper von FramelessWidgetsHelper , die nur den Ruf der void FramelessWidgetsHelper::extendsContentIntoTitleBar() -Funktion für Sie speichern. Sie können stattdessen absolut einfach QWidget verwenden.
Zunächst einmal nennen Sie void FramelessHelper::Widgets::initialize() in Ihrer main in einer sehr frühen Phase ( Muss vor dem Bau eines Q(Gui|Core)Application ):
int main ( int , char **)
{
FramelessHelper::Widgets::initialize ();
// ...
}Verstecken Sie dann die vom Betriebssystem bereitgestellte Standard -Titelleiste:
MyWidget::MyWidget (QWidget *parent) : QWidget(parent)
{
// You should do this early enough.
FramelessWidgetsHelper::get ( this )-> extendsContentIntoTitleBar ();
// ...
} Dann lassen Sie FramelessHelper wissen, was die Titelleiste sein soll:
void MyWidget::myFunction ()
{
// ...
FramelessWidgetsHelper::get ( this )-> setTitleBarWidget (m_myTitleBarWidget);
// ...
}Machen Sie dann einige Widgets in Ihrer Titelleiste sichtbar, um den Test zu treffen:
void MyWidget::myFunction2 ()
{
// ...
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someSearchBox);
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someButton);
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someMenuItem);
// ...
} Wichtiger Hinweis für QT -Widgets -Anwendungen : Einige Funktionen sind möglicherweise nur verfügbar, wenn FramelessHelper den Fensteranpassungsprozess abgeschlossen hat, z. B. das Ändern der Fenstergeometrie/-flags/-status. In diesem Fall können Sie eine Verbindung zum öffentlichen void ready() Signal von FramelessHelper herstellen, um den genauen Zeitpunkt zu erhalten und anschließend Ihren REST -Initialisierungsprozess durchzuführen.
Zunächst sollten Sie void FramelessHelper::Quick::initialize() in Ihrer main in einer sehr frühen Phase bezeichnen ( Muss vor dem Bau eines Q(Gui|Core)Application ):
int main ( int , char **)
{
FramelessHelper::Quick::initialize ();
// ...
} Anschließend müssen Sie die von FramelessHelper bereitgestellten benutzerdefinierten Typen registrieren, indem Sie void FramelessHelper::Quick::registerTypes(QQmlEngine *) anrufen, bevor die QML -Engine alle QML -Dokumente lädt:
int main ( int , char **)
{
// ...
QQmlApplicationEngine engine;
FramelessHelper::Quick::registerTypes (&engine);
// ...
} Jetzt können Sie Ihre QML -Dokumente schreiben. Sie sollten FramelessHelper aus der Uri org.wangwenx190.FramelessHelper importieren. Sie sollten eine Versionsnummer direkt danach angeben, wenn Sie QT5 verwenden:
import org.wangwenx190.FramelessHelper 1.0 // You can use "auto" or omit the version number in Qt6. Und dann können Sie die beigefügten Eigenschaften aus dem QML -Typ FramelessHelper verwenden:
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);
}
}
} Das Gleiche gilt für die FramelessWidgetsHelper -Schnittstelle. Der QML -Typ FramelessHelper wird für jedes Window nur einmal instanziiert, unabhängig davon, wann und wo Sie angehängte Eigenschaften davon verwenden. Aufgrund des speziellen Designs des FramelessHelper -Typs können Sie ihn jedoch auch wie ein normaler QML -Typ verwenden:
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
}
} In der Theorie ist es möglich, mehrere FramelessHelper -Objekte für das gleiche Window zu instanziieren. In diesem Fall wird nur einer von ihnen funktionsfähig, alle anderen Objekte werden zu einem Wrapper davon, aber dies wird nicht empfohlen und kann ein unerwartes Verhalten oder Fehler verursachen. Bitte vermeiden Sie es, dies in jedem Fall zu versuchen.
Wenn Sie feststellen, dass eine von FramelessHelper -Funktionen nach dem Aufrufen keine Wirkung haben, ist der größtmögliche Grund, wenn Sie die Funktion nennen/die Eigenschaft von FramelessHelper ändern, das Stammfenster hat seinen Initialisierungsprozess nicht abgeschlossen, und somit kann FramelessHelper den Handle nicht erhalten, sodass jede Aktion des Benutzers bis zur fertigen Initialisierung des Stammfensters ignoriert wird.
Es gibt auch einen QML -Typ namens FramelessWindow . Es ist nur ein einfacher Wrapper von FramelessHelper , Sie können stattdessen absolut einfaches Window verwenden.
Wichtiger Hinweis für QT -Schnellanwendungen : Einige Funktionen sind möglicherweise nur verfügbar, wenn FramelessHelper den Fensteranpassungsprozess abgeschlossen hat, z. B. das Ändern der Fenstergeometrie/-flags/-status. In diesem Fall können Sie eine Verbindung zum öffentlichen void ready() Signal von FramelessHelper herstellen, um den genauen Zeitpunkt zu erhalten und anschließend Ihren REST -Initialisierungsprozess durchzuführen:
Window {
FramelessHelper . onReady : {
// do something here ...
}
} Window {
FramelessHelper {
onReady : {
// do something here ...
}
}
}Weitere Informationen finden Sie in den Demo -Projekten, um detailliertere Verwendungen zu sehen: Beispiele
Wenn die DWM-Komposition in einigen sehr seltenen Fällen deaktiviert ist (nur unter Windows 7 möglich), erscheint die obere linke Ecke und die obere rechte Ecke in runden Form. Die runden Ecken können auf das Quadrat wiederhergestellt werden, wenn Sie die DWM-Komposition wieder aufnehmen.
Es gibt einen OpenGL-Treiber-Fehler, der dazu führt, dass einige rahmenlose Fenster eine seltsame schwarze Leiste direkt über Ihrer hausgemachten Titelleiste haben. Außerdem werden die Steuerelemente in Ihren Fenstern für einige Pixel in die untere rechte Ecke verschoben. Es ist ein Fehler Ihres Grafikkartentreibers, insbesondere Ihres OpenGL -Treibers, nicht für FramelessHelper. Es gibt einige Lösungen unserer Benutzer, aber einige von ihnen funktionieren möglicherweise nicht unter allen Bedingungen, Sie können eine aus ihnen auswählen:
| Lösung | Prinzip |
|---|---|
| Aktualisieren Sie den Grafiktreiber | Versuchen Sie, einen neueren Fahrer zu verwenden, der möglicherweise mit dem Fix versendet wird |
| Ändern Sie das Systemthema in "grundlegend" (im Gegensatz zu "Windows Aero") | Lassen Sie Windows reine Software -Rendering verwenden |
| Wenn es mehrere Grafikkarten gibt, verwenden Sie stattdessen eine andere | Versuchen Sie, einen anderen Treiber zu verwenden, der möglicherweise überhaupt keinen Fehler hat |
| Aktualisieren Sie das System auf mindestens Windows 11 | Windows 11 hat das Fenstersystem neu gestaltet, damit der Fehler nicht mehr ausgelöst werden kann |
Entfernen Sie die Stile WS_THICKFRAME und WS_OVERLAPPED aus dem Fenster und fügen Sie vielleicht auch den WS_POPUP -Stil gleichzeitig false und tun Sie nichts im Block WM_NCCALCSIZE (geben Sie einfach den gesamten Block aus. | FramelessWindowHint |
Verwenden Sie Qt::FramelessWindowHint , anstatt den Trick WM_NCCALCSIZE zu machen | Der Rendering -Codepfad von QT ist zwischen diesen beiden Lösungen völlig unterschiedlich |
| Erzwingen Sie QT, das Winkel -Backend anstelle des Desktop OpenGL zu verwenden | Angle übersetzt OpenGL -Direktiven in D3D |
| Zweiten Sie QT, reine Software -Rendering zu verwenden, anstatt durch OpenGL zu rendern | QT verwendet OpenGL überhaupt nicht |
| Erzwingen Sie QT, die MESA 3D -Bibliotheken anstelle von normalem OpenGL zu verwenden | Versuchen Sie, eine andere OpenGL -Implementierung zu verwenden |
| Verwenden Sie Direct3D/Vulkan/Metal anstelle von OpenGL | Verwenden Sie einfach nicht den Buggy OpenGL |
Wenn Sie Glück haben, kann einer von ihnen das Problem für Sie beheben. Wenn nicht, können Sie versuchen, mehrere Lösungen zusammen zu verwenden. Ich kann jedoch nicht garantieren, dass das Problem zu 100% behoben werden kann.
Aufgrund vieler Unterversionen von Windows 10 wird dringend empfohlen, die neueste Version von Windows 10 zu verwenden, zumindest nicht älter als Windows 10 1809 . Wenn Sie versuchen, dieses Framework für einige sehr alte Windows 10 -Versionen wie 1507 oder 1607 zu verwenden, gibt es möglicherweise einige Kompatibilitätsprobleme. Die Verwendung dieses Frameworks unter Windows 7 wird ebenfalls unterstützt, aber nicht empfohlen. Um das stabilste Verhalten und das beste Erscheinungsbild zu erhalten, sollten Sie es in der neuesten Version von Windows 10 oder Windows 11 verwenden.
Um das SNAP -Layout wie erwartet funktionieren zu lassen, gibt es einige zusätzliche Regeln für die folgenden hausgemachten Systemtasten:
setSystemButton() für jede Taste (es kann ein beliebiges QWIDGE oder QQUICKItem sein) auf, um FramelessHelper zu informieren, welches die Taste minimieren/maximieren/schließen kann.When running on Win10, it seems the top border is missing? But the demo applications still have it? FramelessHelper verbirgt die Systemtitelleiste, indem er den gesamten oberen Teil des Fensterrahmens einschließlich des oberen Randes entfernt. Es gibt keine Möglichkeit, nur die Systemtitelleiste zu entfernen, aber gleichzeitig die obere Grenze zu bewahren, selbst Microsoft kann dies auch nicht tun. Der genaue Grund ist nicht-Mikrosoft-Entwicklern unbekannt, und ich habe kein Interesse daran, mich in all die Magie dahinter einzugraben. Sie müssen sich also selbst manuell zeichnen, um so zu tun, als ob die obere Grenze noch da ist. Sie können seine Höhe und Farbe durch offizielle DWM -APIs abrufen. Weitere Informationen finden Sie in der Dokumentation von DwmGetWindowAttribute() und DwmGetColorizationColor() . Die Demo -Anwendungen haben immer noch den oberen Rand, da ihre Hauptfenster alle von FramelessWidget oder FramelessMainWindow erben, die die obere Grenze für Sie intern anziehen. Was QT Quick betrifft, so zieht der QML -Typ FramelessWindow auch die obere Grenze.
When running on Wayland, dragging the title bar causes crash? Sie müssen QT zwingen, das XCB QPA beim Laufen auf Wayland zu verwenden. Versuchen Sie, die Umgebungsvariable QT_QPA_PLATFORM (Fallempfindlichkeit) auf xcb (Fallempfindlichkeit) festzulegen, bevor Q(Gui)Application instanziiert. Oder rufen Sie einfach void FramelessHelper::Widgets/Quick::initialize() in Ihrer main auf. Diese Funktion kümmert sich für Sie darum.
I can see the black background during window resizing?Erstens ist es ein QT -Problem, das nicht durch FramelessHelper verursacht wird. Und es sollte für QT -Widget -Anwendungen nicht möglich sein. Es ist ein häufiges Problem für QT -Schnellanwendungen. Meistens wird es durch D3D11/Vulkan/Metal verursacht, da sie nicht gut mit der Änderungsgrößenoperationen zu tun haben. Wenn Sie dieses Problem wirklich beheben möchten, können Sie versuchen, das RHI -Backend von QT in OpenGL (vorsichtig vor dem Fehler Ihres Grafikkartentreibers) oder Software (wenn Sie sich nicht um die Leistung interessieren) zu ändern. Und bitte denken Sie daran, dass dieses Problem nicht von außerhalb von QT behoben werden kann.
Can I preserve the window frame border even on Win7? How does Google Chrome/Microsoft Edge's installer achieve that? Kurze Antwort: Es ist unmöglich. Vollständige Erklärung: Natürlich können wir die gleiche Technik verwenden, die wir bei Win10 verwenden, um den gesamten oberen Teil des Fensters zu entfernen und die anderen drei Frame -Grenzen gleichzeitig zu bewahren. Bei Win10 können wir jedoch die obere Rand zurückbringen, indem wir entweder eine schwarze Magie in der WM_PAINT -Handlerin oder das Zeichnen eines dünnen Rahmengrenats manuell wir selbst in sich selbst erledigen. Ich habe es bereits auf Win7 ausprobiert und leider ist das Ergebnis, dass der WM_PAINT Trick bei Win7 nicht funktioniert, und wir können auch keinen Rahmengrenze zeichnen, der dem ursprünglichen sehr ähnlich aussieht (ein halbtransparentes Rechteck, das mit der Akzentfarbe des Systems und dem visuellen Inhalt hinter dem Fenster gemischt wird, auch mit einem gewissen Unschärfeneffekt). Aber es scheint, dass das Installationsprogramm von Google Chrome/Microsoft Edge das erreicht hat, was wir tun wollten, wie? Nun, ihr Installateur ist Open Source und ich habe bereits Code gelesen. Sie erreichen dies, indem sie zwei Fenster überlappen, ein normales Fenster unten, ein anderes randfreies Fenster oben, um die Titelleiste des unteren Fensters abzudecken. Sie zeichnen ihre hausgemachte Titelleiste auf das randlose Fenster und emulieren sie, um das Verhalten der Standard-Titelleiste zu emulieren. Die vom System bereitgestellte Original -Titelleiste ist immer noch da, kann aber von niemandem gesehen werden, nur weil es von einem anderen Fenster abgedeckt ist. Ich gebe zu, dass es in solchen Fällen eine gute Lösung ist, aber für unsere Bibliothek ist es nicht angemessen, da die Codekomplexität in die Luft jagt.
Bestellt nach der ersten Beitragszeit (es ist möglicherweise nicht sehr genau, sorry)
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.