add basic.

This commit is contained in:
amass 2024-08-31 04:13:21 +08:00
parent 7f5f273e14
commit 8b6bce4e3d
48 changed files with 3029 additions and 632 deletions

View File

@ -2,10 +2,12 @@
#include "Rectangle.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickStyle>
int main(int argc, char *argv[]) {
LOG(info) << "app start...";
QGuiApplication app(argc, argv);
QQuickStyle::setStyle("Basic");
QQmlApplicationEngine engine;
QObject::connect(
&engine, &QQmlApplicationEngine::objectCreationFailed, &app, []() { QCoreApplication::exit(-1); },

4
Fluent/AccentColor.cpp Normal file
View File

@ -0,0 +1,4 @@
#include "AccentColor.h"
AccentColor::AccentColor(QObject *parent) : QObject{parent} {
}

22
Fluent/AccentColor.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef __ACCENTCOLOR_H__
#define __ACCENTCOLOR_H__
#include "Utilities.h"
#include <QObject>
class AccentColor : public QObject {
Q_OBJECT
Q_PROPERTY_AUTO(QColor, darkest)
Q_PROPERTY_AUTO(QColor, darker)
Q_PROPERTY_AUTO(QColor, dark)
Q_PROPERTY_AUTO(QColor, normal)
Q_PROPERTY_AUTO(QColor, light)
Q_PROPERTY_AUTO(QColor, lighter)
Q_PROPERTY_AUTO(QColor, lightest)
QML_ELEMENT
public:
AccentColor(QObject *parent = nullptr);
};
#endif // __ACCENTCOLOR_H__

View File

@ -1,5 +1,5 @@
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Gui Quick)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui Quick)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Gui Quick QuickControls2)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui Quick QuickControls2)
qt_standard_project_setup(REQUIRES 6.5)
@ -16,25 +16,41 @@ qt6_add_qml_module(Fluent
URI Fluent
VERSION 1.0
SOURCES
AccentColor.h AccentColor.cpp
App.h App.cpp
CircularReveal.h CircularReveal.cpp
Colors.h Colors.cpp
Frameless.h Frameless.cpp
Icons.h
Rectangle.h Rectangle.cpp
TextStyle.h TextStyle.cpp
Theme.h Theme.cpp
Utilities.h Utilities.cpp
QML_FILES
qml/Acrylic.qml
qml/AppBar.qml
qml/Button.qml
qml/ContentDialog.qml
qml/ControlBackground.qml
qml/FilledButton.qml
qml/FocusRectangle.qml
qml/Icon.qml
qml/IconButton.qml
qml/ImageButton.qml
qml/InfoBar.qml
qml/Loader.qml
qml/Object.qml
qml/Popup.qml
qml/ProgressRing.qml
qml/Router.qml
qml/ScrollBar.qml
qml/Shadow.qml
qml/Text.qml
qml/Tooltip.qml
qml/Window.qml
RESOURCES
resources/noise.png
resources/FluentIcons.ttf
)
target_include_directories(Fluent
@ -44,4 +60,5 @@ target_include_directories(Fluent
target_link_libraries(Fluent
PUBLIC Qt${QT_VERSION_MAJOR}::Gui
PRIVATE Qt${QT_VERSION_MAJOR}::Quick
INTERFACE Qt${QT_VERSION_MAJOR}::QuickControls2
)

66
Fluent/CircularReveal.cpp Normal file
View File

@ -0,0 +1,66 @@
#include "CircularReveal.h"
#include <QGuiApplication>
#include <QQuickItemGrabResult>
#include <QPainterPath>
CircularReveal::CircularReveal(QQuickItem *parent) : QQuickPaintedItem(parent) {
m_target = nullptr;
m_radius = 0;
_anim = new QPropertyAnimation(this, "radius", this);
_anim->setDuration(333);
_anim->setEasingCurve(QEasingCurve::OutCubic);
setVisible(false);
connect(_anim, &QPropertyAnimation::finished, this, [=]() {
update();
setVisible(false);
Q_EMIT animationFinished();
});
connect(this, &CircularReveal::radiusChanged, this, [=]() { update(); });
}
void CircularReveal::paint(QPainter *painter) {
painter->save();
painter->drawImage(QRect(0, 0, static_cast<int>(width()), static_cast<int>(height())), _source);
QPainterPath path;
path.moveTo(_center.x(), _center.y());
path.addEllipse(QPointF(_center.x(), _center.y()), m_radius, m_radius);
painter->setCompositionMode(QPainter::CompositionMode_Clear);
if (m_darkToLight) {
painter->fillPath(path, Qt::white);
} else {
QPainterPath outerRect;
outerRect.addRect(0, 0, width(), height());
outerRect = outerRect.subtracted(path);
painter->fillPath(outerRect, Qt::black);
}
painter->restore();
}
[[maybe_unused]] void CircularReveal::start(int w, int h, const QPoint &center, int radius) {
if (_anim->state() == QAbstractAnimation::Running) {
_anim->stop();
int currentRadius = m_radius;
_anim->setStartValue(currentRadius);
_anim->setEndValue(m_darkToLight ? 0 : radius);
} else {
if (m_darkToLight) {
_anim->setStartValue(radius);
_anim->setEndValue(0);
} else {
_anim->setStartValue(0);
_anim->setEndValue(radius);
}
}
_center = center;
_grabResult = m_target->grabToImage(QSize(w, h));
connect(_grabResult.data(), &QQuickItemGrabResult::ready, this,
&CircularReveal::handleGrabResult);
}
void CircularReveal::handleGrabResult() {
_grabResult.data()->image().swap(_source);
update();
setVisible(true);
Q_EMIT imageChanged();
_anim->start();
}

31
Fluent/CircularReveal.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef __CIRCULARREVEAL_H__
#define __CIRCULARREVEAL_H__
#include "Utilities.h"
#include <QPainter>
#include <QPropertyAnimation>
#include <QQuickItem>
#include <QQuickPaintedItem>
class CircularReveal : public QQuickPaintedItem {
Q_OBJECT
QML_ELEMENT
Q_PROPERTY_AUTO_P(QQuickItem *, target)
Q_PROPERTY_AUTO(int, radius)
Q_PROPERTY_AUTO(bool, darkToLight)
public:
explicit CircularReveal(QQuickItem *parent = nullptr);
void paint(QPainter *painter) override;
Q_INVOKABLE void start(int w, int h, const QPoint &center, int radius);
Q_SIGNAL void imageChanged();
Q_SIGNAL void animationFinished();
Q_SLOT void handleGrabResult();
private:
QPropertyAnimation *_anim = nullptr;
QImage _source;
QPoint _center;
QSharedPointer<QQuickItemGrabResult> _grabResult;
};
#endif // __CIRCULARREVEAL_H__

135
Fluent/Colors.cpp Normal file
View File

@ -0,0 +1,135 @@
#include "Colors.h"
Colors *Colors::instance() {
static Colors *self = nullptr;
if (self == nullptr) {
self = new Colors();
}
return self;
}
Colors *Colors::create(QQmlEngine *, QJSEngine *) {
auto ret = instance();
QJSEngine::setObjectOwnership(ret, QJSEngine::CppOwnership);
return ret;
}
Colors::Colors(QObject *parent) : QObject{parent} {
m_Transparent = QColor(0, 0, 0, 0);
m_Black = QColor(0, 0, 0);
m_White = QColor(255, 255, 255);
m_Grey10 = QColor(250, 249, 248);
m_Grey20 = QColor(243, 242, 241);
m_Grey30 = QColor(237, 235, 233);
m_Grey40 = QColor(225, 223, 221);
m_Grey50 = QColor(210, 208, 206);
m_Grey60 = QColor(200, 198, 196);
m_Grey70 = QColor(190, 185, 184);
m_Grey80 = QColor(179, 176, 173);
m_Grey90 = QColor(161, 159, 157);
m_Grey100 = QColor(151, 149, 146);
m_Grey110 = QColor(138, 136, 134);
m_Grey120 = QColor(121, 119, 117);
m_Grey130 = QColor(96, 94, 92);
m_Grey140 = QColor(72, 70, 68);
m_Grey150 = QColor(59, 58, 57);
m_Grey160 = QColor(50, 49, 48);
m_Grey170 = QColor(41, 40, 39);
m_Grey180 = QColor(37, 36, 35);
m_Grey190 = QColor(32, 31, 30);
m_Grey200 = QColor(27, 26, 25);
m_Grey210 = QColor(22, 21, 20);
m_Grey220 = QColor(17, 16, 15);
auto yellow = new AccentColor(this);
yellow->darkest(QColor(249, 168, 37));
yellow->darker(QColor(251, 192, 45));
yellow->dark(QColor(253, 212, 53));
yellow->normal(QColor(255, 235, 59));
yellow->light(QColor(255, 238, 88));
yellow->lighter(QColor(255, 241, 118));
yellow->lightest(QColor(255, 245, 155));
m_Yellow = yellow;
auto orange = new AccentColor(this);
orange->darkest(QColor(153, 61, 7));
orange->darker(QColor(172, 68, 8));
orange->dark(QColor(209, 88, 10));
orange->normal(QColor(247, 99, 12));
orange->light(QColor(248, 122, 48));
orange->lighter(QColor(249, 145, 84));
orange->lightest(QColor(250, 192, 106));
m_Orange = orange;
auto red = new AccentColor(this);
red->darkest(QColor(143, 10, 21));
red->darker(QColor(162, 11, 24));
red->dark(QColor(185, 13, 28));
red->normal(QColor(232, 17, 35));
red->light(QColor(236, 64, 79));
red->lighter(QColor(238, 88, 101));
red->lightest(QColor(240, 107, 118));
m_Red = red;
auto magenta = new AccentColor(this);
magenta->darkest(QColor(111, 0, 79));
magenta->darker(QColor(160, 7, 108));
magenta->dark(QColor(181, 13, 125));
magenta->normal(QColor(227, 0, 140));
magenta->light(QColor(234, 77, 168));
magenta->lighter(QColor(238, 110, 193));
magenta->lightest(QColor(241, 140, 213));
m_Magenta = magenta;
auto purple = new AccentColor(this);
purple->darkest(QColor(44, 15, 118));
purple->darker(QColor(61, 15, 153));
purple->dark(QColor(78, 17, 174));
purple->normal(QColor(104, 33, 122));
purple->light(QColor(123, 76, 157));
purple->lighter(QColor(141, 110, 189));
purple->lightest(QColor(158, 142, 217));
m_Purple = purple;
auto blue = new AccentColor(this);
blue->darkest(QColor(0, 74, 131));
blue->darker(QColor(0, 84, 148));
blue->dark(QColor(0, 102, 180));
blue->normal(QColor(0, 120, 212));
blue->light(QColor(38, 140, 220));
blue->lighter(QColor(76, 160, 224));
blue->lightest(QColor(96, 171, 228));
m_Blue = blue;
auto teal = new AccentColor(this);
teal->darkest(QColor(0, 110, 91));
teal->darker(QColor(0, 124, 103));
teal->dark(QColor(0, 151, 125));
teal->normal(QColor(0, 178, 148));
teal->light(QColor(38, 189, 164));
teal->lighter(QColor(77, 201, 180));
teal->lightest(QColor(96, 207, 188));
m_Teal = teal;
auto green = new AccentColor(this);
green->darkest(QColor(9, 76, 9));
green->darker(QColor(12, 93, 12));
green->dark(QColor(14, 111, 14));
green->normal(QColor(16, 124, 16));
green->light(QColor(39, 137, 57));
green->lighter(QColor(76, 156, 76));
green->lightest(QColor(106, 173, 106));
m_Green = green;
}
AccentColor *Colors::createAccentColor(const QColor &primaryColor) {
auto accentColor = new AccentColor(this);
accentColor->normal(primaryColor);
accentColor->dark(Utilities::instance()->withOpacity(primaryColor, 0.9));
accentColor->light(Utilities::instance()->withOpacity(primaryColor, 0.9));
accentColor->darker(Utilities::instance()->withOpacity(accentColor->dark(), 0.8));
accentColor->lighter(Utilities::instance()->withOpacity(accentColor->light(), 0.8));
accentColor->darkest(Utilities::instance()->withOpacity(accentColor->darker(), 0.7));
accentColor->lightest(Utilities::instance()->withOpacity(accentColor->lighter(), 0.7));
return accentColor;
}

56
Fluent/Colors.h Normal file
View File

@ -0,0 +1,56 @@
#ifndef __COLORS_H__
#define __COLORS_H__
#include "AccentColor.h"
#include "Utilities.h"
#include <QObject>
class Colors : public QObject {
Q_OBJECT
Q_PROPERTY_AUTO(QColor, Transparent)
Q_PROPERTY_AUTO(QColor, Black)
Q_PROPERTY_AUTO(QColor, White)
Q_PROPERTY_AUTO(QColor, Grey10)
Q_PROPERTY_AUTO(QColor, Grey20)
Q_PROPERTY_AUTO(QColor, Grey30)
Q_PROPERTY_AUTO(QColor, Grey40)
Q_PROPERTY_AUTO(QColor, Grey50)
Q_PROPERTY_AUTO(QColor, Grey60)
Q_PROPERTY_AUTO(QColor, Grey70)
Q_PROPERTY_AUTO(QColor, Grey80)
Q_PROPERTY_AUTO(QColor, Grey90)
Q_PROPERTY_AUTO(QColor, Grey100)
Q_PROPERTY_AUTO(QColor, Grey110)
Q_PROPERTY_AUTO(QColor, Grey120)
Q_PROPERTY_AUTO(QColor, Grey130)
Q_PROPERTY_AUTO(QColor, Grey140)
Q_PROPERTY_AUTO(QColor, Grey150)
Q_PROPERTY_AUTO(QColor, Grey160)
Q_PROPERTY_AUTO(QColor, Grey170)
Q_PROPERTY_AUTO(QColor, Grey180)
Q_PROPERTY_AUTO(QColor, Grey190)
Q_PROPERTY_AUTO(QColor, Grey200)
Q_PROPERTY_AUTO(QColor, Grey210)
Q_PROPERTY_AUTO(QColor, Grey220)
Q_PROPERTY_AUTO_P(AccentColor *, Yellow)
Q_PROPERTY_AUTO_P(AccentColor *, Orange)
Q_PROPERTY_AUTO_P(AccentColor *, Red)
Q_PROPERTY_AUTO_P(AccentColor *, Magenta)
Q_PROPERTY_AUTO_P(AccentColor *, Purple)
Q_PROPERTY_AUTO_P(AccentColor *, Blue)
Q_PROPERTY_AUTO_P(AccentColor *, Teal)
Q_PROPERTY_AUTO_P(AccentColor *, Green)
QML_ELEMENT
QML_SINGLETON
public:
static Colors *instance();
static Colors *create(QQmlEngine *, QJSEngine *);
Q_INVOKABLE AccentColor *createAccentColor(const QColor &primaryColor);
protected:
Colors(QObject *parent = nullptr);
};
#endif // __COLORS_H__

View File

@ -1,4 +1,5 @@
#include "Frameless.h"
#include "Theme.h"
#include "Utilities.h"
#include <QQuickWindow>
#include <dwmapi.h>
@ -19,6 +20,121 @@ static inline void setShadow(HWND hwnd) {
}
}
enum WINDOWCOMPOSITIONATTRIB {
WCA_UNDEFINED = 0,
WCA_NCRENDERING_ENABLED = 1,
WCA_NCRENDERING_POLICY = 2,
WCA_TRANSITIONS_FORCEDISABLED = 3,
WCA_ALLOW_NCPAINT = 4,
WCA_CAPTION_BUTTON_BOUNDS = 5,
WCA_NONCLIENT_RTL_LAYOUT = 6,
WCA_FORCE_ICONIC_REPRESENTATION = 7,
WCA_EXTENDED_FRAME_BOUNDS = 8,
WCA_HAS_ICONIC_BITMAP = 9,
WCA_THEME_ATTRIBUTES = 10,
WCA_NCRENDERING_EXILED = 11,
WCA_NCADORNMENTINFO = 12,
WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
WCA_VIDEO_OVERLAY_ACTIVE = 14,
WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
WCA_DISALLOW_PEEK = 16,
WCA_CLOAK = 17,
WCA_CLOAKED = 18,
WCA_ACCENT_POLICY = 19,
WCA_FREEZE_REPRESENTATION = 20,
WCA_EVER_UNCLOAKED = 21,
WCA_VISUAL_OWNER = 22,
WCA_HOLOGRAPHIC = 23,
WCA_EXCLUDED_FROM_DDA = 24,
WCA_PASSIVEUPDATEMODE = 25,
WCA_USEDARKMODECOLORS = 26,
WCA_CORNER_STYLE = 27,
WCA_PART_COLOR = 28,
WCA_DISABLE_MOVESIZE_FEEDBACK = 29,
WCA_LAST = 30
};
enum _DWM_SYSTEMBACKDROP_TYPE {
_DWMSBT_AUTO, // [Default] Let DWM automatically decide the system-drawn backdrop for this
// window.
_DWMSBT_NONE, // [Disable] Do not draw any system backdrop.
_DWMSBT_MAINWINDOW, // [Mica] Draw the backdrop material effect corresponding to a
// long-lived window.
_DWMSBT_TRANSIENTWINDOW, // [Acrylic] Draw the backdrop material effect corresponding to a
// transient window.
_DWMSBT_TABBEDWINDOW, // [Mica Alt] Draw the backdrop material effect corresponding to a
// window with a tabbed title bar.
};
enum ACCENT_STATE {
ACCENT_DISABLED = 0,
ACCENT_ENABLE_GRADIENT = 1,
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
ACCENT_ENABLE_BLURBEHIND = 3, // Traditional DWM blur
ACCENT_ENABLE_ACRYLICBLURBEHIND = 4, // RS4 1803
ACCENT_ENABLE_HOST_BACKDROP = 5, // RS5 1809
ACCENT_INVALID_STATE = 6 // Using this value will remove the window background
};
enum ACCENT_FLAG {
ACCENT_NONE = 0,
ACCENT_ENABLE_ACRYLIC = 1,
ACCENT_ENABLE_ACRYLIC_WITH_LUMINOSITY = 482,
};
struct WINDOWCOMPOSITIONATTRIBDATA {
WINDOWCOMPOSITIONATTRIB Attrib;
PVOID pvData;
SIZE_T cbData;
};
using PWINDOWCOMPOSITIONATTRIBDATA = WINDOWCOMPOSITIONATTRIBDATA *;
struct ACCENT_POLICY {
DWORD dwAccentState;
DWORD dwAccentFlags;
DWORD dwGradientColor; // #AABBGGRR
DWORD dwAnimationId;
};
using PACCENT_POLICY = ACCENT_POLICY *;
typedef HRESULT(WINAPI *DwmSetWindowAttributeFunc)(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute,
DWORD cbAttribute);
typedef HRESULT(WINAPI *DwmExtendFrameIntoClientAreaFunc)(HWND hwnd, const MARGINS *pMarInset);
typedef HRESULT(WINAPI *DwmIsCompositionEnabledFunc)(BOOL *pfEnabled);
typedef HRESULT(WINAPI *DwmEnableBlurBehindWindowFunc)(HWND hWnd, const DWM_BLURBEHIND *pBlurBehind);
typedef BOOL(WINAPI *SetWindowCompositionAttributeFunc)(HWND hwnd, const WINDOWCOMPOSITIONATTRIBDATA *);
static DwmSetWindowAttributeFunc pDwmSetWindowAttribute = nullptr;
static DwmExtendFrameIntoClientAreaFunc pDwmExtendFrameIntoClientArea = nullptr;
static DwmIsCompositionEnabledFunc pDwmIsCompositionEnabled = nullptr;
static DwmEnableBlurBehindWindowFunc pDwmEnableBlurBehindWindow = nullptr;
static SetWindowCompositionAttributeFunc pSetWindowCompositionAttribute = nullptr;
static RTL_OSVERSIONINFOW GetRealOSVersionImpl() {
HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
using RtlGetVersionPtr = NTSTATUS(WINAPI *)(PRTL_OSVERSIONINFOW);
auto pRtlGetVersion = reinterpret_cast<RtlGetVersionPtr>(::GetProcAddress(hMod, "RtlGetVersion"));
RTL_OSVERSIONINFOW rovi{};
rovi.dwOSVersionInfoSize = sizeof(rovi);
pRtlGetVersion(&rovi);
return rovi;
}
RTL_OSVERSIONINFOW GetRealOSVersion() {
static const auto result = GetRealOSVersionImpl();
return result;
}
static inline bool isWin7Only() {
RTL_OSVERSIONINFOW rovi = GetRealOSVersion();
return rovi.dwMajorVersion = 7;
}
static inline bool isWin11OrGreater() {
RTL_OSVERSIONINFOW rovi = GetRealOSVersion();
return (rovi.dwMajorVersion > 10) ||
(rovi.dwMajorVersion == 10 && rovi.dwMinorVersion >= 0 && rovi.dwBuildNumber >= 22000);
}
static bool containsCursorToItem(QQuickItem *item) {
auto window = item->window();
if ((window == nullptr) || !item || !item->isVisible()) {
@ -32,8 +148,189 @@ static bool containsCursorToItem(QQuickItem *item) {
return false;
}
static inline bool initializeFunctionPointers() {
HMODULE module = LoadLibraryW(L"dwmapi.dll");
if (module) {
if (!pDwmSetWindowAttribute) {
pDwmSetWindowAttribute =
reinterpret_cast<DwmSetWindowAttributeFunc>(GetProcAddress(module, "DwmSetWindowAttribute"));
if (!pDwmSetWindowAttribute) {
return false;
}
}
if (!pDwmExtendFrameIntoClientArea) {
pDwmExtendFrameIntoClientArea = reinterpret_cast<DwmExtendFrameIntoClientAreaFunc>(
GetProcAddress(module, "DwmExtendFrameIntoClientArea"));
if (!pDwmExtendFrameIntoClientArea) {
return false;
}
}
if (!pDwmIsCompositionEnabled) {
pDwmIsCompositionEnabled =
reinterpret_cast<DwmIsCompositionEnabledFunc>(::GetProcAddress(module, "DwmIsCompositionEnabled"));
if (!pDwmIsCompositionEnabled) {
return false;
}
}
if (!pDwmEnableBlurBehindWindow) {
pDwmEnableBlurBehindWindow =
reinterpret_cast<DwmEnableBlurBehindWindowFunc>(GetProcAddress(module, "DwmEnableBlurBehindWindow"));
if (!pDwmEnableBlurBehindWindow) {
return false;
}
}
if (!pSetWindowCompositionAttribute) {
HMODULE user32 = LoadLibraryW(L"user32.dll");
if (!user32) {
return false;
}
pSetWindowCompositionAttribute = reinterpret_cast<SetWindowCompositionAttributeFunc>(
GetProcAddress(user32, "SetWindowCompositionAttribute"));
if (!pSetWindowCompositionAttribute) {
return false;
}
}
}
return true;
}
static inline bool isWin1122H2OrGreater() {
RTL_OSVERSIONINFOW rovi = GetRealOSVersion();
return (rovi.dwMajorVersion > 10) ||
(rovi.dwMajorVersion == 10 && rovi.dwMinorVersion >= 0 && rovi.dwBuildNumber >= 22621);
}
static inline bool setWindowDarkMode(HWND hwnd, const BOOL enable) {
if (!initializeFunctionPointers()) {
return false;
}
return bool(pDwmSetWindowAttribute(hwnd, 20, &enable, sizeof(BOOL)));
}
static inline bool isCompositionEnabled() {
if (initializeFunctionPointers()) {
BOOL composition_enabled = false;
pDwmIsCompositionEnabled(&composition_enabled);
return composition_enabled;
}
return false;
}
static inline bool isWin8OrGreater() {
RTL_OSVERSIONINFOW rovi = GetRealOSVersion();
return (rovi.dwMajorVersion > 6) || (rovi.dwMajorVersion == 6 && rovi.dwMinorVersion >= 2);
}
static inline bool setWindowEffect(HWND hwnd, const QString &key, const bool &enable) {
static constexpr const MARGINS extendedMargins = {-1, -1, -1, -1};
if (key == QStringLiteral("mica")) {
if (!isWin11OrGreater() || !initializeFunctionPointers()) {
return false;
}
if (enable) {
pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins);
if (isWin1122H2OrGreater()) {
const DWORD backdropType = _DWMSBT_MAINWINDOW;
pDwmSetWindowAttribute(hwnd, 38, &backdropType, sizeof(backdropType));
} else {
const BOOL enable = TRUE;
pDwmSetWindowAttribute(hwnd, 1029, &enable, sizeof(enable));
}
} else {
if (isWin1122H2OrGreater()) {
const DWORD backdropType = _DWMSBT_AUTO;
pDwmSetWindowAttribute(hwnd, 38, &backdropType, sizeof(backdropType));
} else {
const BOOL enable = FALSE;
pDwmSetWindowAttribute(hwnd, 1029, &enable, sizeof(enable));
}
}
BOOL isDark = Theme::instance()->dark();
setWindowDarkMode(hwnd, isDark);
return true;
}
if (key == QStringLiteral("mica-alt")) {
if (!isWin1122H2OrGreater() || !initializeFunctionPointers()) {
return false;
}
if (enable) {
pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins);
const DWORD backdropType = _DWMSBT_TABBEDWINDOW;
pDwmSetWindowAttribute(hwnd, 38, &backdropType, sizeof(backdropType));
} else {
const DWORD backdropType = _DWMSBT_AUTO;
pDwmSetWindowAttribute(hwnd, 38, &backdropType, sizeof(backdropType));
}
BOOL isDark = Theme::instance()->dark();
setWindowDarkMode(hwnd, isDark);
return true;
}
if (key == QStringLiteral("acrylic")) {
if (!isWin11OrGreater() || !initializeFunctionPointers()) {
return false;
}
if (enable) {
pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins);
DWORD system_backdrop_type = _DWMSBT_TRANSIENTWINDOW;
pDwmSetWindowAttribute(hwnd, 38, &system_backdrop_type, sizeof(DWORD));
} else {
const DWORD backdropType = _DWMSBT_AUTO;
pDwmSetWindowAttribute(hwnd, 38, &backdropType, sizeof(backdropType));
}
BOOL isDark = Theme::instance()->dark();
setWindowDarkMode(hwnd, isDark);
return true;
}
if (key == QStringLiteral("dwm-blur")) {
if ((isWin7Only() && !isCompositionEnabled()) || !initializeFunctionPointers()) {
return false;
}
BOOL isDark = Theme::instance()->dark();
setWindowDarkMode(hwnd, isDark && enable);
if (enable) {
if (isWin8OrGreater()) {
ACCENT_POLICY policy{};
policy.dwAccentState = ACCENT_ENABLE_BLURBEHIND;
policy.dwAccentFlags = ACCENT_NONE;
WINDOWCOMPOSITIONATTRIBDATA wcad{};
wcad.Attrib = WCA_ACCENT_POLICY;
wcad.pvData = &policy;
wcad.cbData = sizeof(policy);
pSetWindowCompositionAttribute(hwnd, &wcad);
} else {
DWM_BLURBEHIND bb{};
bb.fEnable = TRUE;
bb.dwFlags = DWM_BB_ENABLE;
pDwmEnableBlurBehindWindow(hwnd, &bb);
}
} else {
if (isWin8OrGreater()) {
ACCENT_POLICY policy{};
policy.dwAccentState = ACCENT_DISABLED;
policy.dwAccentFlags = ACCENT_NONE;
WINDOWCOMPOSITIONATTRIBDATA wcad{};
wcad.Attrib = WCA_ACCENT_POLICY;
wcad.pvData = &policy;
wcad.cbData = sizeof(policy);
pSetWindowCompositionAttribute(hwnd, &wcad);
} else {
DWM_BLURBEHIND bb{};
bb.fEnable = FALSE;
bb.dwFlags = DWM_BB_ENABLE;
pDwmEnableBlurBehindWindow(hwnd, &bb);
}
}
return true;
}
return false;
}
Frameless::Frameless(QQuickItem *parent) : QQuickItem{parent} {
m_isWindows11OrGreater = Utilities::instance()->isWindows11OrGreater();
m_effect = "normal";
}
QQuickItem *Frameless::appBar() const {
@ -119,12 +416,36 @@ void Frameless::setHitTestVisible(QQuickItem *item) {
}
}
void Frameless::showMaximized() {
#ifdef Q_OS_WIN
HWND hwnd = reinterpret_cast<HWND>(window()->winId());
::ShowWindow(hwnd, 3);
#else
window()->setVisibility(QQuickWindow::Maximized);
#endif
}
void Frameless::showMinimized() {
#ifdef Q_OS_WIN
HWND hwnd = reinterpret_cast<HWND>(window()->winId());
::ShowWindow(hwnd, 2);
#else
window()->setVisibility(QQuickWindow::Minimized);
#endif
}
void Frameless::showNormal() {
window()->setVisibility(QQuickWindow::Windowed);
}
void Frameless::onDestruction() {
QGuiApplication::instance()->removeNativeEventFilter(this);
}
void Frameless::componentComplete() {
if (m_disabled) return;
if (m_disabled) {
return;
}
int w = window()->width();
int h = window()->height();
m_current = window()->winId();
@ -150,8 +471,12 @@ void Frameless::componentComplete() {
#endif
HWND hwnd = reinterpret_cast<HWND>(window()->winId());
DWORD style = ::GetWindowLongPtr(hwnd, GWL_STYLE);
#if (QT_VERSION == QT_VERSION_CHECK(6, 7, 2))
style &= ~(WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU);
#endif
if (m_fixSize) {
::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_THICKFRAME | WS_CAPTION);
;
for (int i = 0; i <= QGuiApplication::screens().count() - 1; ++i) {
connect(QGuiApplication::screens().at(i), &QScreen::logicalDotsPerInchChanged, this, [=] {
SetWindowPos(hwnd, nullptr, 0, 0, 0, 0,
@ -171,6 +496,42 @@ void Frameless::componentComplete() {
if (!window()->property("_hideShadow").toBool()) {
setShadow(hwnd);
}
if (isWin11OrGreater()) {
availableEffects({"mica", "mica-alt", "acrylic", "dwm-blur", "normal"});
} else if (isWin7Only()) {
availableEffects({"dwm-blur", "normal"});
}
if (!m_effect.isEmpty()) {
effective(setWindowEffect(hwnd, m_effect, true));
if (effective()) {
m_currentEffect = effect();
}
}
connect(this, &Frameless::effectChanged, this, [hwnd, this] {
if (effect() == m_currentEffect) {
return;
}
if (effective()) {
setWindowEffect(hwnd, m_currentEffect, false);
}
effective(setWindowEffect(hwnd, effect(), true));
if (effective()) {
m_currentEffect = effect();
} else {
m_effect = "normal";
m_currentEffect = "normal";
}
});
connect(Theme::instance(), &Theme::blurBehindWindowEnabledChanged, this, [this] {
if (Theme::instance()->blurBehindWindowEnabled()) {
effect("normal");
}
});
connect(Theme::instance(), &Theme::darkChanged, this, [hwnd, this] {
if (effective() && !m_currentEffect.isEmpty() && m_currentEffect != "normal") {
setWindowDarkMode(hwnd, Theme::instance()->dark());
}
});
#endif
auto appBarHeight = m_appBar->height();
h = qRound(h + appBarHeight);

View File

@ -1,12 +1,16 @@
#ifndef FRAMELESS_H
#define FRAMELESS_H
#include "Utilities.h"
#include <QAbstractNativeEventFilter>
#include <QQuickItem>
class Frameless : public QQuickItem, QAbstractNativeEventFilter {
Q_OBJECT
QML_ELEMENT
Q_PROPERTY_AUTO(QString, effect)
Q_PROPERTY_READONLY_AUTO(bool, effective)
Q_PROPERTY_READONLY_AUTO(QStringList, availableEffects)
Q_PROPERTY(QQuickItem *appBar READ appBar WRITE setAppBar NOTIFY appBarChanged)
Q_PROPERTY(QQuickItem *maximizeButton READ maximizeButton WRITE setMaximizeButton NOTIFY maximizeButtonChanged)
Q_PROPERTY(QQuickItem *minimizedButton READ minimizedButton WRITE setMinimizedButton NOTIFY minimizedButtonChanged)
@ -39,6 +43,10 @@ public:
bool disabled() const;
void setDisabled(bool disabled);
Q_INVOKABLE void showMaximized();
Q_INVOKABLE void showMinimized();
Q_INVOKABLE void showNormal();
Q_INVOKABLE void setHitTestVisible(QQuickItem *item);
Q_INVOKABLE void onDestruction();
void componentComplete() final;
@ -77,6 +85,7 @@ private:
int m_margins = 8;
QList<QPointer<QQuickItem>> m_hitTestList;
bool m_isWindows11OrGreater = false;
QString m_currentEffect;
};
#endif // FRAMELESS_H

48
Fluent/TextStyle.cpp Normal file
View File

@ -0,0 +1,48 @@
#include "TextStyle.h"
TextStyle::TextStyle(QObject *parent) : QObject{parent} {
m_family = QFont().defaultFamily();
#ifdef Q_OS_WIN
m_family = "微软雅黑";
#endif
QFont caption;
caption.setFamily(m_family);
caption.setPixelSize(12);
Caption(caption);
QFont body;
body.setFamily(m_family);
body.setPixelSize(13);
Body(body);
QFont bodyStrong;
bodyStrong.setFamily(m_family);
bodyStrong.setPixelSize(13);
bodyStrong.setWeight(QFont::DemiBold);
BodyStrong(bodyStrong);
QFont subtitle;
subtitle.setFamily(m_family);
subtitle.setPixelSize(20);
subtitle.setWeight(QFont::DemiBold);
Subtitle(subtitle);
QFont title;
title.setFamily(m_family);
title.setPixelSize(28);
title.setWeight(QFont::DemiBold);
Title(title);
QFont titleLarge;
titleLarge.setFamily(m_family);
titleLarge.setPixelSize(40);
titleLarge.setWeight(QFont::DemiBold);
TitleLarge(titleLarge);
QFont display;
display.setFamily(m_family);
display.setPixelSize(68);
display.setWeight(QFont::DemiBold);
Display(display);
}

25
Fluent/TextStyle.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef __TEXTSTYLE_H__
#define __TEXTSTYLE_H__
#include "Utilities.h"
#include <QFont>
#include <QObject>
class TextStyle : public QObject {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_PROPERTY_AUTO(QString, family)
Q_PROPERTY_AUTO(QFont, Caption)
Q_PROPERTY_AUTO(QFont, Body)
Q_PROPERTY_AUTO(QFont, BodyStrong)
Q_PROPERTY_AUTO(QFont, Subtitle)
Q_PROPERTY_AUTO(QFont, Title)
Q_PROPERTY_AUTO(QFont, TitleLarge)
Q_PROPERTY_AUTO(QFont, Display)
public:
TextStyle(QObject *parent = nullptr);
};
#endif // __TEXTSTYLE_H__

View File

@ -1,6 +1,56 @@
#include "Theme.h"
#include "Colors.h"
#include <QGuiApplication>
#include <QPalette>
#include <QThreadPool>
static bool systemDark() {
QPalette palette = QGuiApplication::palette();
QColor color = palette.color(QPalette::Window).rgb();
return color.red() * 0.2126 + color.green() * 0.7152 + color.blue() * 0.0722 <= 255.0f / 2;
}
Theme *Theme::instance() {
static Theme *self = nullptr;
if (self == nullptr) {
self = new Theme();
}
return self;
}
Theme *Theme::create(QQmlEngine *, QJSEngine *) {
auto ret = instance();
QJSEngine::setObjectOwnership(ret, QJSEngine::CppOwnership);
return ret;
}
Theme::Theme(QObject *parent) : QObject{parent} {
m_accentColor = Colors::instance()->Blue();
m_darkMode = ThemeType::DarkMode::Light;
m_nativeText = false;
m_animationEnabled = true;
m_systemDark = systemDark();
m_desktopImagePath = "";
m_blurBehindWindowEnabled = false;
QGuiApplication::instance()->installEventFilter(this);
refreshColors();
connect(this, &Theme::darkModeChanged, this, [=] { Q_EMIT darkChanged(); });
connect(this, &Theme::darkChanged, this, [=] { refreshColors(); });
connect(this, &Theme::accentColorChanged, this, [=] { refreshColors(); });
connect(&m_watcher, &QFileSystemWatcher::fileChanged, this,
[=](const QString &path) { Q_EMIT desktopImagePathChanged(); });
connect(this, &Theme::blurBehindWindowEnabledChanged, this, [=] { checkUpdateDesktopImage(); });
startTimer(1000);
}
bool Theme::dark() const {
if (m_darkMode == ThemeType::DarkMode::Dark) {
return true;
} else if (m_darkMode == ThemeType::DarkMode::System) {
return m_systemDark;
} else {
return false;
}
}
QColor Theme::fontPrimaryColor() const {
@ -79,3 +129,53 @@ void Theme::setBlurBehindWindowEnabled(bool enabled) {
emit blurBehindWindowEnabledChanged();
}
}
void Theme::refreshColors() {
auto isDark = dark();
primaryColor(isDark ? m_accentColor->lighter() : m_accentColor->dark());
backgroundColor(isDark ? QColor(0, 0, 0, 255) : QColor(255, 255, 255, 255));
dividerColor(isDark ? QColor(80, 80, 80, 255) : QColor(210, 210, 210, 255));
setWindowBackgroundColor(isDark ? QColor(32, 32, 32, 255) : QColor(237, 237, 237, 255));
setWindowActiveBackgroundColor(isDark ? QColor(26, 26, 26, 255) : QColor(243, 243, 243, 255));
setFontPrimaryColor(isDark ? QColor(248, 248, 248, 255) : QColor(7, 7, 7, 255));
fontSecondaryColor(isDark ? QColor(222, 222, 222, 255) : QColor(102, 102, 102, 255));
fontTertiaryColor(isDark ? QColor(200, 200, 200, 255) : QColor(153, 153, 153, 255));
setItemNormalColor(isDark ? QColor(255, 255, 255, 0) : QColor(0, 0, 0, 0));
frameColor(isDark ? QColor(56, 56, 56, qRound(255 * 0.8)) : QColor(243, 243, 243, qRound(255 * 0.8)));
frameActiveColor(isDark ? QColor(48, 48, 48, qRound(255 * 0.8)) : QColor(255, 255, 255, qRound(255 * 0.8)));
setItemHoverColor(isDark ? QColor(255, 255, 255, qRound(255 * 0.06)) : QColor(0, 0, 0, qRound(255 * 0.03)));
itemPressColor(isDark ? QColor(255, 255, 255, qRound(255 * 0.09)) : QColor(0, 0, 0, qRound(255 * 0.06)));
itemCheckColor(isDark ? QColor(255, 255, 255, qRound(255 * 0.12)) : QColor(0, 0, 0, qRound(255 * 0.09)));
}
void Theme::checkUpdateDesktopImage() {
if (!m_blurBehindWindowEnabled) {
return;
}
QThreadPool::globalInstance()->start([=]() {
m_mutex.lock();
auto path = Utilities::instance()->getWallpaperFilePath();
if (m_desktopImagePath != path) {
if (!m_desktopImagePath.isEmpty()) {
m_watcher.removePath(m_desktopImagePath);
}
setDesktopImagePath(path);
m_watcher.addPath(path);
}
m_mutex.unlock();
});
}
bool Theme::eventFilter(QObject *, QEvent *event) {
if (event->type() == QEvent::ApplicationPaletteChange || event->type() == QEvent::ThemeChange) {
m_systemDark = systemDark();
Q_EMIT darkChanged();
event->accept();
return true;
}
return false;
}
void Theme::timerEvent(QTimerEvent *event) {
checkUpdateDesktopImage();
}

View File

@ -1,7 +1,11 @@
#ifndef THEME_H
#define THEME_H
#include "AccentColor.h"
#include "Utilities.h"
#include <QColor>
#include <QFileSystemWatcher>
#include <QMutex>
#include <QObject>
#include <QQmlEngine>
@ -9,7 +13,21 @@ class Theme : public QObject {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_PROPERTY(bool dark READ dark NOTIFY darkChanged)
Q_PROPERTY_AUTO_P(AccentColor *, accentColor)
Q_PROPERTY_AUTO(int, darkMode)
Q_PROPERTY_AUTO(QColor, primaryColor)
Q_PROPERTY_AUTO(QColor, backgroundColor)
Q_PROPERTY_AUTO(QColor, dividerColor)
Q_PROPERTY_AUTO(QColor, itemPressColor)
Q_PROPERTY_AUTO(bool, nativeText)
Q_PROPERTY_AUTO(bool, animationEnabled)
Q_PROPERTY(QColor fontPrimaryColor READ fontPrimaryColor WRITE setFontPrimaryColor NOTIFY fontPrimaryColorChanged)
Q_PROPERTY_AUTO(QColor, fontSecondaryColor)
Q_PROPERTY_AUTO(QColor, fontTertiaryColor)
Q_PROPERTY_AUTO(QColor, frameColor)
Q_PROPERTY_AUTO(QColor, frameActiveColor)
Q_PROPERTY_AUTO(QColor, itemCheckColor)
Q_PROPERTY(QColor itemNormalColor READ itemNormalColor WRITE setItemNormalColor NOTIFY itemNormalColorChanged)
Q_PROPERTY(QColor itemHoverColor READ itemHoverColor WRITE setItemHoverColor NOTIFY itemHoverColorChanged)
@ -23,7 +41,9 @@ class Theme : public QObject {
blurBehindWindowEnabledChanged)
public:
Theme(QObject *parent = nullptr);
static Theme *instance();
static Theme *create(QQmlEngine *, QJSEngine *);
bool dark() const;
QColor fontPrimaryColor() const;
void setFontPrimaryColor(const QColor &color);
@ -47,6 +67,7 @@ public:
void setBlurBehindWindowEnabled(bool enabled);
signals:
void darkChanged();
void fontPrimaryColorChanged();
void itemNormalColorChanged();
void windowBackgroundColorChanged();
@ -55,7 +76,17 @@ signals:
void blurBehindWindowEnabledChanged();
void itemHoverColorChanged();
protected:
Theme(QObject *parent = nullptr);
void refreshColors();
void checkUpdateDesktopImage();
void timerEvent(QTimerEvent *event) final;
bool eventFilter(QObject *obj, QEvent *event) final;
private:
bool m_systemDark = false;
QMutex m_mutex;
QFileSystemWatcher m_watcher;
QColor m_fontPrimaryColor;
QColor m_itemNormalColor;
QColor m_itemHoverColor;

View File

@ -1,5 +1,6 @@
#include "Utilities.h"
#include <QSettings>
#include <qt_windows.h>
Utilities *Utilities::instance() {
static Utilities *self = nullptr;
@ -18,10 +19,70 @@ Utilities *Utilities::create(QQmlEngine *, QJSEngine *) {
Utilities::Utilities(QObject *parent) : QObject{parent} {
}
bool Utilities::isSoftware() {
return QQuickWindow::sceneGraphBackend() == "software";
}
void Utilities::deleteLater(QObject *p) {
if (p) {
p->deleteLater();
}
}
QColor Utilities::withOpacity(const QColor &color, qreal opacity) {
int alpha = qRound(opacity * 255) & 0xff;
return QColor::fromRgba((alpha << 24) | (color.rgba() & 0xffffff));
}
QRect Utilities::desktopAvailableGeometry(QQuickWindow *window) {
return window->screen()->availableGeometry();
}
QString Utilities::getWallpaperFilePath() {
#if defined(Q_OS_WIN)
wchar_t path[MAX_PATH] = {};
if (::SystemParametersInfoW(SPI_GETDESKWALLPAPER, MAX_PATH, path, FALSE) == FALSE) {
return {};
}
return QString::fromWCharArray(path);
#elif defined(Q_OS_LINUX)
auto type = QSysInfo::productType();
if (type == "uos") {
QProcess process;
QStringList args;
args << "--session";
args << "--type=method_call";
args << "--print-reply";
args << "--dest=com.deepin.wm";
args << "/com/deepin/wm";
args << "com.deepin.wm.GetCurrentWorkspaceBackgroundForMonitor";
args << QString("string:'%1'").arg(currentTimestamp());
process.start("dbus-send", args);
process.waitForFinished();
QByteArray result = process.readAllStandardOutput().trimmed();
int startIndex = result.indexOf("file:///");
if (startIndex != -1) {
auto path = result.mid(startIndex + 7, result.length() - startIndex - 8);
return path;
}
}
#elif defined(Q_OS_MACOS)
QProcess process;
QStringList args;
args << "-e";
args << R"(tell application "Finder" to get POSIX path of (desktop picture as alias))";
process.start("osascript", args);
process.waitForFinished();
QByteArray result = process.readAllStandardOutput().trimmed();
if (result.isEmpty()) {
return "/System/Library/CoreServices/DefaultDesktop.heic";
}
return result;
#else
return {};
#endif
}
QUrl Utilities::getUrlByFilePath(const QString &path) {
return QUrl::fromLocalFile(path);
}

View File

@ -5,6 +5,84 @@
#include <QQmlEngine>
#include <QQuickWindow>
#define Q_PROPERTY_AUTO(TYPE, M) \
Q_PROPERTY(TYPE M MEMBER m_##M NOTIFY M##Changed) \
public: \
Q_SIGNAL void M##Changed(); \
void M(const TYPE &in_##M) { \
m_##M = in_##M; \
Q_EMIT M##Changed(); \
} \
TYPE M() { \
return m_##M; \
} \
\
private: \
TYPE m_##M;
#define Q_PROPERTY_READONLY_AUTO(TYPE, M) \
Q_PROPERTY(TYPE M READ M NOTIFY M##Changed FINAL) \
public: \
Q_SIGNAL void M##Changed(); \
void M(const TYPE &in_##M) { \
m_##M = in_##M; \
Q_EMIT M##Changed(); \
} \
TYPE M() { \
return m_##M; \
} \
\
private: \
TYPE m_##M;
#define Q_PROPERTY_AUTO_P(TYPE, M) \
Q_PROPERTY(TYPE M MEMBER m_##M NOTIFY M##Changed) \
public: \
Q_SIGNAL void M##Changed(); \
void M(TYPE in_##M) { \
m_##M = in_##M; \
Q_EMIT M##Changed(); \
} \
TYPE M() { \
return m_##M; \
} \
\
private: \
TYPE m_##M;
namespace WindowType {
Q_NAMESPACE
enum LaunchMode {
Standard = 0x0000,
SingleTask = 0x0001,
SingleInstance = 0x0002,
};
Q_ENUM_NS(LaunchMode)
QML_ELEMENT
} // namespace WindowType
namespace ThemeType {
Q_NAMESPACE
enum DarkMode {
System = 0x0000,
Light = 0x0001,
Dark = 0x0002,
};
Q_ENUM_NS(DarkMode)
QML_ELEMENT
} // namespace ThemeType
namespace ContentDialogType {
Q_NAMESPACE
enum ButtonFlag {
NeutralButton = 0x0001,
NegativeButton = 0x0002,
PositiveButton = 0x0004,
};
Q_ENUM_NS(ButtonFlag)
QML_ELEMENT
} // namespace ContentDialogType
class Utilities : public QObject {
Q_OBJECT
QML_ELEMENT
@ -18,6 +96,10 @@ public:
Q_INVOKABLE bool isMacos();
Q_INVOKABLE QRect desktopAvailableGeometry(QQuickWindow *window);
Q_INVOKABLE QUrl getUrlByFilePath(const QString &path);
Q_INVOKABLE bool isSoftware();
Q_INVOKABLE void deleteLater(QObject *p);
Q_INVOKABLE QColor withOpacity(const QColor &, qreal alpha);
Q_INVOKABLE QString getWallpaperFilePath();
protected:
Utilities(QObject *parent = nullptr);

View File

@ -1,33 +1,33 @@
import QtQuick
Item {
id: control
property color tintColor: Qt.rgba(1, 1, 1, 1)
property real tintOpacity: 0.65
property real luminosity: 0.01
property real noiseOpacity: 0.02
property var target
property int blurRadius: 32
property rect targetRect: Qt.rect(control.x, control.y, control.width,control.height)
ShaderEffectSource {
id: effect_source
anchors.fill: parent
visible: false
sourceRect: control.targetRect
sourceItem: control.target
}
Rectangle {
anchors.fill: parent
color: Qt.rgba(1, 1, 1, luminosity)
}
Rectangle {
anchors.fill: parent
color: Qt.rgba(tintColor.r, tintColor.g, tintColor.b, tintOpacity)
}
Image {
anchors.fill: parent
source: "qrc:/qt/qml/Fluent/resources/noise.png"
fillMode: Image.Tile
opacity: control.noiseOpacity
}
}
import QtQuick
Item {
id: control
property color tintColor: Qt.rgba(1, 1, 1, 1)
property real tintOpacity: 0.65
property real luminosity: 0.01
property real noiseOpacity: 0.02
property var target
property int blurRadius: 32
property rect targetRect: Qt.rect(control.x, control.y, control.width,control.height)
ShaderEffectSource {
id: effect_source
anchors.fill: parent
visible: false
sourceRect: control.targetRect
sourceItem: control.target
}
Rectangle {
anchors.fill: parent
color: Qt.rgba(1, 1, 1, luminosity)
}
Rectangle {
anchors.fill: parent
color: Qt.rgba(tintColor.r, tintColor.g, tintColor.b, tintOpacity)
}
Image {
anchors.fill: parent
source: "qrc:/qt/qml/Fluent/resources/noise.png"
fillMode: Image.Tile
opacity: control.noiseOpacity
}
}

View File

@ -1,87 +1,281 @@
import QtQuick as Quick
import QtQuick.Layouts
import Fluent
Quick.Rectangle {
id: root
property bool showMinimize: true
property bool showMaximize: true
property bool showClose: true
property bool showStayTop: true
property bool showDark: false
property string title: ""
property url icon
property string maximizeText : qsTr("Maximize")
property Quick.color textColor: Theme.fontPrimaryColor
property Quick.color maximizeNormalColor: Theme.itemNormalColor
property Quick.color maximizeHoverColor: Theme.itemHoverColor
property bool isMac: Utilities.isMacos()
property alias buttonMaximize: btn_maximize
property alias layoutMacosButtons: layout_macos_buttons
property alias layoutStandardbuttons: layout_standard_buttons
Quick.Item{
id:d
property var hitTestList: []
property bool hoverMaxBtn: false
property var win: Window.window
property bool stayTop: {
if(d.win instanceof Window){
return d.win.stayTop
}
return false
}
property bool isRestore: win && (Window.Maximized === win.visibility || Window.FullScreen === win.visibility)
property bool resizable: win && !(win.height === win.maximumHeight && win.height === win.minimumHeight && win.width === win.maximumWidth && win.width === win.minimumWidth)
function containsPointToItem(point,item){
var pos = item.mapToGlobal(0,0)
var rect = Qt.rect(pos.x,pos.y,item.width,item.height)
if(point.x>rect.x && point.x<(rect.x+rect.width) && point.y>rect.y && point.y<(rect.y+rect.height)){
return true
}
return false
}
}
RowLayout {
id:layout_standard_buttons
height: parent.height
anchors.right: parent.right
spacing: 0
IconButton{
id:btn_maximize
property bool hover: btn_maximize.hovered
Layout.preferredWidth: 40
Layout.preferredHeight: 30
padding: 0
verticalPadding: 0
horizontalPadding: 0
iconSource : d.isRestore ? Icons.ChromeRestore : Icons.ChromeMaximize
color: {
if(down){
return maximizePressColor
}
return btn_maximize.hover ? maximizeHoverColor : maximizeNormalColor
}
Layout.alignment: Qt.AlignVCenter
visible: d.resizable && !isMac && showMaximize
radius: 0
iconColor: root.textColor
text:d.isRestore?restoreText:maximizeText
iconSize: 11
onClicked: maxClickListener()
}
}
Quick.Loader{
id:layout_macos_buttons
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: 10
}
sourceComponent: isMac ? com_macos_buttons : undefined
Quick.Component.onDestruction: sourceComponent = undefined
}
}
import QtQuick as Quick
import QtQuick.Controls
import QtQuick.Window
import QtQuick.Layouts
import Fluent
Quick.Rectangle{
property string title: ""
property string darkText : qsTr("Dark")
property string lightText : qsTr("Light")
property string minimizeText : qsTr("Minimize")
property string restoreText : qsTr("Restore")
property string maximizeText : qsTr("Maximize")
property string closeText : qsTr("Close")
property string stayTopText : qsTr("Sticky on Top")
property string stayTopCancelText : qsTr("Sticky on Top cancelled")
property color textColor: Theme.fontPrimaryColor
property color minimizeNormalColor: Theme.itemNormalColor
property color minimizeHoverColor: Theme.itemHoverColor
property color minimizePressColor: Theme.itemPressColor
property color maximizeNormalColor: Theme.itemNormalColor
property color maximizeHoverColor: Theme.itemHoverColor
property color maximizePressColor: Theme.itemPressColor
property color closeNormalColor: Qt.rgba(0,0,0,0)
property color closeHoverColor: Qt.rgba(251/255,115/255,115/255,1)
property color closePressColor: Qt.rgba(251/255,115/255,115/255,0.8)
property bool showDark: false
property bool showClose: true
property bool showMinimize: true
property bool showMaximize: true
property bool showStayTop: true
property bool titleVisible: true
property url icon
property int iconSize: 20
property bool isMac: Utilities.isMacos()
property color borerlessColor : Theme.primaryColor
property alias buttonStayTop: btn_stay_top
property alias buttonMinimize: btn_minimize
property alias buttonMaximize: btn_maximize
property alias buttonClose: btn_close
property alias buttonDark: btn_dark
property alias layoutMacosButtons: layout_macos_buttons
property alias layoutStandardbuttons: layout_standard_buttons
property var maxClickListener : function(){
if(Utilities.isMacos()){
if (d.win.visibility === Window.FullScreen || d.win.visibility === Window.Maximized)
d.win.showNormal()
else
d.win.showFullScreen()
}else{
if (d.win.visibility === Window.Maximized || d.win.visibility === Window.FullScreen)
d.win.showNormal()
else
d.win.showMaximized()
d.hoverMaxBtn = false
}
}
property var minClickListener: function(){
if(d.win.transientParent != null){
d.win.transientParent.showMinimized()
}else{
d.win.showMinimized()
}
}
property var closeClickListener : function(){
d.win.close()
}
property var stayTopClickListener: function(){
if(d.win instanceof Window){
d.win.stayTop = !d.win.stayTop
}
}
property var darkClickListener: function(){
if(Theme.dark){
Theme.darkMode = ThemeType.Light
}else{
Theme.darkMode = ThemeType.Dark
}
}
id:control
color: Qt.rgba(0,0,0,0)
height: visible ? 30 : 0
opacity: visible
z: 65535
Item{
id:d
property var hitTestList: []
property bool hoverMaxBtn: false
property var win: Window.window
property bool stayTop: {
if(d.win instanceof Window){
return d.win.stayTop
}
return false
}
property bool isRestore: win && (Window.Maximized === win.visibility || Window.FullScreen === win.visibility)
property bool resizable: win && !(win.height === win.maximumHeight && win.height === win.minimumHeight && win.width === win.maximumWidth && win.width === win.minimumWidth)
function containsPointToItem(point,item){
var pos = item.mapToGlobal(0,0)
var rect = Qt.rect(pos.x,pos.y,item.width,item.height)
if(point.x>rect.x && point.x<(rect.x+rect.width) && point.y>rect.y && point.y<(rect.y+rect.height)){
return true
}
return false
}
}
Row{
anchors{
verticalCenter: parent.verticalCenter
left: isMac ? undefined : parent.left
leftMargin: isMac ? undefined : 10
horizontalCenter: isMac ? parent.horizontalCenter : undefined
}
spacing: 10
Image{
width: control.iconSize
height: control.iconSize
visible: status === Image.Ready ? true : false
source: control.icon
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: title
visible: control.titleVisible
color:control.textColor
anchors.verticalCenter: parent.verticalCenter
}
}
Component{
id:com_macos_buttons
RowLayout{
ImageButton{
Layout.preferredHeight: 12
Layout.preferredWidth: 12
normalImage: "../Image/btn_close_normal.png"
hoveredImage: "../Image/btn_close_hovered.png"
pushedImage: "../Image/btn_close_pushed.png"
visible: showClose
onClicked: closeClickListener()
}
ImageButton{
Layout.preferredHeight: 12
Layout.preferredWidth: 12
normalImage: "../Image/btn_min_normal.png"
hoveredImage: "../Image/btn_min_hovered.png"
pushedImage: "../Image/btn_min_pushed.png"
onClicked: minClickListener()
visible: showMinimize
}
ImageButton{
Layout.preferredHeight: 12
Layout.preferredWidth: 12
normalImage: "../Image/btn_max_normal.png"
hoveredImage: "../Image/btn_max_hovered.png"
pushedImage: "../Image/btn_max_pushed.png"
onClicked: maxClickListener()
visible: d.resizable && showMaximize
}
}
}
RowLayout{
id:layout_standard_buttons
height: parent.height
anchors.right: parent.right
spacing: 0
IconButton{
id:btn_dark
Layout.preferredWidth: 40
Layout.preferredHeight: 30
padding: 0
verticalPadding: 0
horizontalPadding: 0
rightPadding: 2
iconSource: Theme.dark ? Icons.Brightness : Icons.QuietHours
Layout.alignment: Qt.AlignVCenter
iconSize: 15
visible: showDark
text: Theme.dark ? control.lightText : control.darkText
radius: 0
iconColor:control.textColor
onClicked:()=> darkClickListener(btn_dark)
}
IconButton{
id:btn_stay_top
Layout.preferredWidth: 40
Layout.preferredHeight: 30
padding: 0
verticalPadding: 0
horizontalPadding: 0
iconSource : Icons.Pinned
Layout.alignment: Qt.AlignVCenter
iconSize: 14
visible: {
if(!(d.win instanceof Window)){
return false
}
return showStayTop
}
text:d.stayTop ? control.stayTopCancelText : control.stayTopText
radius: 0
iconColor: d.stayTop ? Theme.primaryColor : control.textColor
onClicked: stayTopClickListener()
}
IconButton{
id:btn_minimize
Layout.preferredWidth: 40
Layout.preferredHeight: 30
padding: 0
verticalPadding: 0
horizontalPadding: 0
iconSource : Icons.ChromeMinimize
Layout.alignment: Qt.AlignVCenter
iconSize: 11
text:minimizeText
radius: 0
visible: !isMac && showMinimize
iconColor: control.textColor
color: {
if(pressed){
return minimizePressColor
}
return hovered ? minimizeHoverColor : minimizeNormalColor
}
onClicked: minClickListener()
}
IconButton{
id:btn_maximize
property bool hover: btn_maximize.hovered
Layout.preferredWidth: 40
Layout.preferredHeight: 30
padding: 0
verticalPadding: 0
horizontalPadding: 0
iconSource : d.isRestore ? Icons.ChromeRestore : Icons.ChromeMaximize
color: {
if(down){
return maximizePressColor
}
return btn_maximize.hover ? maximizeHoverColor : maximizeNormalColor
}
Layout.alignment: Qt.AlignVCenter
visible: d.resizable && !isMac && showMaximize
radius: 0
iconColor: control.textColor
text:d.isRestore?restoreText:maximizeText
iconSize: 11
onClicked: maxClickListener()
}
IconButton{
id:btn_close
Layout.preferredWidth: 40
Layout.preferredHeight: 30
padding: 0
verticalPadding: 0
horizontalPadding: 0
iconSource : Icons.ChromeClose
Layout.alignment: Qt.AlignVCenter
text:closeText
visible: !isMac && showClose
radius: 0
iconSize: 10
iconColor: hovered ? Qt.rgba(1,1,1,1) : control.textColor
color:{
if(pressed){
return closePressColor
}
return hovered ? closeHoverColor : closeNormalColor
}
onClicked: closeClickListener()
}
}
Loader{
id:layout_macos_buttons
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: 10
}
sourceComponent: isMac ? com_macos_buttons : undefined
}
}

64
Fluent/qml/Button.qml Normal file
View File

@ -0,0 +1,64 @@
import QtQuick as Quick
import QtQuick.Controls as Control
import Fluent
Control.Button {
property bool disabled: false
property string contentDescription: ""
property Quick.color normalColor: Theme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(254/255,254/255,254/255,1)
property Quick.color hoverColor: Theme.dark ? Qt.rgba(68/255,68/255,68/255,1) : Qt.rgba(246/255,246/255,246/255,1)
property Quick.color disableColor: Theme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(251/255,251/255,251/255,1)
property Quick.color dividerColor: Theme.dark ? Qt.rgba(80/255,80/255,80/255,1) : Qt.rgba(233/255,233/255,233/255,1)
property Quick.color textColor: {
if(Theme.dark){
if(!enabled){
return Qt.rgba(131/255,131/255,131/255,1)
}
if(pressed){
return Qt.rgba(162/255,162/255,162/255,1)
}
return Qt.rgba(1,1,1,1)
}else{
if(!enabled){
return Qt.rgba(160/255,160/255,160/255,1)
}
if(pressed){
return Qt.rgba(96/255,96/255,96/255,1)
}
return Qt.rgba(0,0,0,1)
}
}
Quick.Accessible.role: Quick.Accessible.Button
Quick.Accessible.name: control.text
Quick.Accessible.description: contentDescription
Quick.Accessible.onPressAction: control.clicked()
id: control
enabled: !disabled
verticalPadding: 0
horizontalPadding:12
font:TextStyle.Body
focusPolicy:Qt.TabFocus
background: ControlBackground{
implicitWidth: 30
implicitHeight: 30
radius: 4
color: {
if(!enabled){
return disableColor
}
return hovered ? hoverColor :normalColor
}
shadow: !pressed && enabled
FocusRectangle{
visible: control.activeFocus
radius:4
}
}
contentItem: Text {
text: control.text
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font: control.font
color: control.textColor
}
}

View File

@ -0,0 +1,164 @@
import QtQuick as Quick
import QtQuick.Layouts
import QtQuick.Controls as Controls
import QtQuick.Window
import Fluent
Popup {
id: control
property string title: ""
property string message: ""
property string neutralText: qsTr("Close")
property string negativeText: qsTr("Cancel")
property string positiveText: qsTr("OK")
property int messageTextFormart: Text.AutoText
property int delayTime: 100
property int buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
property var contentDelegate: Component{
Item{
}
}
property var onNeutralClickListener
property var onNegativeClickListener
property var onPositiveClickListener
signal neutralClicked
signal negativeClicked
signal positiveClicked
implicitWidth: 400
implicitHeight: layout_content.height
focus: true
Component{
id:com_message
Flickable{
id:sroll_message
contentHeight: text_message.height
contentWidth: width
clip: true
boundsBehavior:Flickable.StopAtBounds
width: parent.width
height: message === "" ? 0 : Math.min(text_message.height,300)
Controls.ScrollBar.vertical: ScrollBar {}
Text{
id:text_message
font: TextStyle.Body
wrapMode: Text.WrapAnywhere
text:message
width: parent.width
topPadding: 4
leftPadding: 20
rightPadding: 20
bottomPadding: 4
}
}
}
Rectangle {
id:layout_content
width: parent.width
height: layout_column.childrenRect.height
color: 'transparent'
radius:5
ColumnLayout{
id:layout_column
width: parent.width
Text{
id:text_title
font: TextStyle.Title
text:title
topPadding: 20
leftPadding: 20
rightPadding: 20
wrapMode: Text.WrapAnywhere
}
Loader{
sourceComponent: com_message
Layout.fillWidth: true
Layout.preferredHeight: status===Loader.Ready ? item.height : 0
}
Loader{
sourceComponent:control.visible ? control.contentDelegate : undefined
Layout.fillWidth: true
onStatusChanged: {
if(status===Loader.Ready){
Layout.preferredHeight = item.implicitHeight
}else{
Layout.preferredHeight = 0
}
}
}
Rectangle{
id:layout_actions
Layout.fillWidth: true
Layout.preferredHeight: 60
radius: 5
color: Theme.dark ? Qt.rgba(32/255,32/255,32/255,1) : Qt.rgba(243/255,243/255,243/255,1)
RowLayout{
anchors
{
centerIn: parent
margins: spacing
fill: parent
}
spacing: 10
Item{
Layout.fillWidth: true
Layout.fillHeight: true
Button{
id:neutral_btn
visible: control.buttonFlags&ContentDialogType.NeutralButton
text: neutralText
width: parent.width
anchors.centerIn: parent
onClicked: {
if(control.onNeutralClickListener){
control.onNeutralClickListener()
}else{
neutralClicked()
control.close()
}
}
}
}
Item{
Layout.fillWidth: true
Layout.fillHeight: true
Button{
id:negative_btn
visible: control.buttonFlags&ContentDialogType.NegativeButton
width: parent.width
anchors.centerIn: parent
text: negativeText
onClicked: {
if(control.onNegativeClickListener){
control.onNegativeClickListener()
}else{
negativeClicked()
control.close()
}
}
}
}
Item{
Layout.fillWidth: true
Layout.fillHeight: true
FilledButton{
id:positive_btn
visible: control.buttonFlags&ContentDialogType.PositiveButton
text: positiveText
width: parent.width
anchors.centerIn: parent
onClicked: {
if(control.onPositiveClickListener){
control.onPositiveClickListener()
}else{
positiveClicked()
control.close()
}
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,52 @@
import QtQuick as Quick
import QtQuick.Controls
import Fluent
Quick.Item{
id:control
property int radius: 4
property bool shadow: true
property alias border: d.border
property var bottomMargin: undefined
property var topMargin: undefined
property var leftMargin: undefined
property var rightMargin: undefined
property Quick.color color: Theme.dark ? Qt.rgba(42/255,42/255,42/255,1) : Qt.rgba(254/255,254/255,254/255,1)
property alias gradient : rect_border.gradient
Quick.Rectangle{
id:d
property Quick.color startColor: Qt.lighter(d.border.color,1.25)
property Quick.color endColor: shadow ? control.border.color : startColor
visible: false
border.color: Theme.dark ? Qt.rgba(48/255,48/255,48/255,1) : Qt.rgba(188/255,188/255,188/255,1)
}
Quick.Rectangle{
id:rect_border
anchors.fill: parent
radius: control.radius
gradient: Quick.Gradient {
Quick.GradientStop { position: 0.0; color: d.startColor }
Quick.GradientStop { position: 1 - 3/control.height; color: d.startColor }
Quick.GradientStop { position: 1.0; color: d.endColor}
}
}
Quick.Rectangle{
id:rect_back
anchors{
fill: parent
margins: control.border.width
topMargin: control.topMargin
bottomMargin: control.bottomMargin
leftMargin: control.leftMargin
rightMargin: control.rightMargin
}
Quick.Behavior on anchors.bottomMargin {
Quick.NumberAnimation{
easing.type: Easing.OutCubic
duration: 167
}
}
radius: control.radius
color: control.color
}
}

View File

@ -0,0 +1,60 @@
import QtQuick as Quick
import QtQuick.Controls
import Fluent
Button {
property bool disabled: false
property string contentDescription: ""
property Quick.color normalColor: Theme.primaryColor
property Quick.color hoverColor: Theme.dark ? Qt.darker(normalColor,1.1) : Qt.lighter(normalColor,1.1)
property Quick.color disableColor: Theme.dark ? Qt.rgba(82/255,82/255,82/255,1) : Qt.rgba(199/255,199/255,199/255,1)
property Quick.color pressedColor: Theme.dark ? Qt.darker(normalColor,1.2) : Qt.lighter(normalColor,1.2)
property Quick.color textColor: {
if(Theme.dark){
if(!enabled){
return Qt.rgba(173/255,173/255,173/255,1)
}
return Qt.rgba(0,0,0,1)
}else{
return Qt.rgba(1,1,1,1)
}
}
Quick.Accessible.role: Quick.Accessible.Button
Quick.Accessible.name: control.text
Quick.Accessible.description: contentDescription
Quick.Accessible.onPressAction: control.clicked()
id: control
enabled: !disabled
focusPolicy:Qt.TabFocus
font:TextStyle.Body
verticalPadding: 0
horizontalPadding:12
background: ControlBackground{
implicitWidth: 30
implicitHeight: 30
radius: 4
bottomMargin: enabled ? 2 : 0
border.width: enabled ? 1 : 0
border.color: enabled ? Qt.darker(control.normalColor,1.2) : disableColor
color:{
if(!enabled){
return disableColor
}
if(pressed){
return pressedColor
}
return hovered ? hoverColor :normalColor
}
FocusRectangle{
visible: control.visualFocus
radius:4
}
}
contentItem: Text {
text: control.text
font: control.font
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: control.textColor
}
}

View File

@ -0,0 +1,20 @@
import QtQuick as Quick
import QtQuick.Controls
import Fluent
Quick.Item {
property int radius: 4
id:control
anchors.fill: parent
Quick.Rectangle{
width: control.width
height: control.height
anchors.centerIn: parent
color: "#00000000"
border.width: 2
radius: control.radius
border.color: Theme.dark ? Qt.rgba(1,1,1,1) : Qt.rgba(0,0,0,1)
z: 65535
}
}

View File

@ -1,19 +1,21 @@
import QtQuick
Text {
property int iconSource
property int iconSize: 20
property color iconColor: FluTheme.dark ? "#FFFFFF" : "#000000"
id:control
font.family: font_loader.name
font.pixelSize: iconSize
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: iconColor
text: (String.fromCharCode(iconSource).toString(16))
opacity: iconSource>0
FontLoader{
id: font_loader
source: "qrc:/qt/qml/FluentUI/Font/FluentIcons.ttf"
}
}
import QtQuick
import QtQuick.Controls
import Fluent
Text {
property int iconSource
property int iconSize: 20
property color iconColor: Theme.dark ? "#FFFFFF" : "#000000"
id:control
font.family: font_loader.name
font.pixelSize: iconSize
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: iconColor
text: (String.fromCharCode(iconSource).toString(16))
opacity: iconSource>0
FontLoader{
id: font_loader
source: "qrc:/qt/qml/Fluent/resources/FluentIcons.ttf"
}
}

View File

@ -1,30 +1,129 @@
import QtQuick
import QtQuick.Controls
Button {
property int iconSize: 20
property int iconSource
property int radius:4
property color color: {
if(!enabled){
return disableColor
}
if(pressed){
return pressedColor
}
return hovered ? hoverColor : normalColor
}
property color iconColor: {
if (FluTheme.dark) {
if (!enabled) {
return Qt.rgba(130 / 255, 130 / 255, 130 / 255, 1)
}
return Qt.rgba(1, 1, 1, 1)
} else {
if (!enabled) {
return Qt.rgba(161 / 255, 161 / 255, 161 / 255, 1)
}
return Qt.rgba(0, 0, 0, 1)
}
}
}
import QtQuick as Quick
import QtQuick.Controls as Controls
import QtQuick.Layouts
import Fluent
Controls.Button {
display: Controls.Button.IconOnly
property int iconSize: 20
property int iconSource
property bool disabled: false
property int radius:4
property string contentDescription: ""
property Quick.color hoverColor: Theme.itemHoverColor
property Quick.color pressedColor: Theme.itemPressColor
property Quick.color normalColor: Theme.itemNormalColor
property Quick.color disableColor: Theme.itemNormalColor
property Quick.Component iconDelegate: com_icon
property Quick.color color: {
if(!enabled){
return disableColor
}
if(pressed){
return pressedColor
}
return hovered ? hoverColor : normalColor
}
property Quick.color iconColor: {
if(Theme.dark){
if(!enabled){
return Qt.rgba(130/255,130/255,130/255,1)
}
return Qt.rgba(1,1,1,1)
}else{
if(!enabled){
return Qt.rgba(161/255,161/255,161/255,1)
}
return Qt.rgba(0,0,0,1)
}
}
property Quick.color textColor: Theme.fontPrimaryColor
Quick.Accessible.role: Quick.Accessible.Button
Quick.Accessible.name: control.text
Quick.Accessible.description: contentDescription
Quick.Accessible.onPressAction: control.clicked()
id:control
focusPolicy:Qt.TabFocus
padding: 0
verticalPadding: 8
horizontalPadding: 8
enabled: !disabled
font:TextStyle.Caption
background: Quick.Rectangle{
implicitWidth: 30
implicitHeight: 30
radius: control.radius
color:control.color
FocusRectangle{
visible: control.activeFocus
}
}
Quick.Component{
id:com_icon
Icon {
id:text_icon
font.pixelSize: iconSize
iconSize: control.iconSize
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
iconColor: control.iconColor
iconSource: control.iconSource
}
}
Quick.Component{
id:com_row
RowLayout{
Loader{
sourceComponent: iconDelegate
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
visible: display !== Button.TextOnly
}
Text{
text:control.text
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
visible: display !== Button.IconOnly
color: control.textColor
font: control.font
}
}
}
Quick.Component{
id:com_column
ColumnLayout{
Loader{
sourceComponent: iconDelegate
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
visible: display !== Button.TextOnly
}
Text{
text:control.text
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
visible: display !== Button.IconOnly
color: control.textColor
font: control.font
}
}
}
contentItem:Loader{
sourceComponent: {
if(display === Button.TextUnderIcon){
return com_column
}
return com_row
}
}
Tooltip{
id:tool_tip
visible: {
if(control.text === ""){
return false
}
if(control.display !== Button.IconOnly){
return false
}
return hovered
}
text:control.text
delay: 1000
}
}

48
Fluent/qml/Image.qml Normal file
View File

@ -0,0 +1,48 @@
import QtQuick
import QtQuick.Controls
import FluentUI
Image {
property string errorButtonText: qsTr("Reload")
property var clickErrorListener : function(){
image.source = ""
image.source = control.source
}
property Component errorItem : com_error
property Component loadingItem: com_loading
id: control
FluLoader{
anchors.fill: parent
sourceComponent: {
if(control.status === Image.Loading){
return com_loading
}else if(control.status == Image.Error){
return com_error
}else{
return undefined
}
}
}
Component{
id:com_loading
Rectangle{
color: FluTheme.itemHoverColor
FluProgressRing{
anchors.centerIn: parent
visible: control.status === Image.Loading
}
}
}
Component{
id:com_error
Rectangle{
color: FluTheme.itemHoverColor
FluFilledButton{
text: control.errorButtonText
anchors.centerIn: parent
visible: control.status === Image.Error
onClicked: clickErrorListener()
}
}
}
}

View File

@ -0,0 +1,17 @@
import QtQuick
import QtQuick.Controls as Controls
Controls.Button{
id:control
property string normalImage: ""
property string hoveredImage: ""
property string pushedImage: ""
background: Item{
implicitHeight: 12
implicitWidth: 12
BorderImage {
anchors.fill: parent
source: control.hovered ? (control.pressed ? control.pushedImage : control.hoveredImage ) : control.normalImage
}
}
}

View File

@ -1,245 +1,243 @@
import QtQuick as Quick
import QtQuick.Controls
import Fluent
Object {
property var root
property int layoutY: 75
id:control
Object{
id:mcontrol
property string const_success: "success"
property string const_info: "info"
property string const_warning: "warning"
property string const_error: "error"
property int maxWidth: 300
property var screenLayout: null
function create(type,text,duration,moremsg){
if(screenLayout){
var last = screenLayout.getLastloader()
if(last.type === type && last.text === text && moremsg === last.moremsg){
last.duration = duration
if (duration > 0) last.restart()
return last
}
}
initScreenLayout()
return contentComponent.createObject(screenLayout,{type:type,text:text,duration:duration,moremsg:moremsg,})
}
function createCustom(itemcomponent,duration){
initScreenLayout()
if(itemcomponent){
return contentComponent.createObject(screenLayout,{itemcomponent:itemcomponent,duration:duration})
}
}
function initScreenLayout(){
if(screenLayout == null){
screenLayout = screenlayoutComponent.createObject(root)
screenLayout.y = control.layoutY
screenLayout.z = 100000
}
}
Quick.Component {
id:screenlayoutComponent
Quick.Column{
parent: Overlay.overlay
z:999
spacing: 20
width: root.width
move: Quick.Transition {
Quick.NumberAnimation {
properties: "y"
easing.type: Easing.OutCubic
duration: FluTheme.animationEnabled ? 333 : 0
}
}
onChildrenChanged: if(children.length === 0) destroy()
function getLastloader(){
if(children.length > 0){
return children[children.length - 1]
}
return null
}
}
}
Quick.Component{
id:contentComponent
Quick.Item{
id:content
property int duration: 1500
property var itemcomponent
property string type
property string text
property string moremsg
width: parent.width
height: loader.height
function close(){
content.destroy()
}
function restart(){
delayTimer.restart()
}
Quick.Timer {
id:delayTimer
interval: duration
running: duration > 0
repeat: duration > 0
onTriggered: content.close()
}
Quick.Loader{
id:loader
x:(parent.width - width) / 2
property var _super: content
scale: item ? 1 : 0
asynchronous: true
Quick.Behavior on scale {
enabled: FluTheme.animationEnabled
Quick.NumberAnimation {
easing.type: Easing.OutCubic
duration: 167
}
}
sourceComponent:itemcomponent ? itemcomponent : mcontrol.fluent_sytle
Quick.Component.onDestruction: sourceComponent = undefined
}
}
}
property Quick.Component fluent_sytle: Quick.Rectangle{
width: rowlayout.width + (btn_close.visible ? 30 : 48)
height: rowlayout.height + 20
color: {
if(FluTheme.dark){
switch(_super.type){
case mcontrol.const_success: return Qt.rgba(57/255,61/255,27/255,1)
case mcontrol.const_warning: return Qt.rgba(67/255,53/255,25/255,1)
case mcontrol.const_info: return Qt.rgba(39/255,39/255,39/255,1)
case mcontrol.const_error: return Qt.rgba(68/255,39/255,38/255,1)
}
return Qt.rgba(1,1,1,1)
}else{
switch(_super.type){
case mcontrol.const_success: return Qt.rgba(223/255,246/255,221/255,1)
case mcontrol.const_warning: return Qt.rgba(255/255,244/255,206/255,1)
case mcontrol.const_info: return Qt.rgba(244/255,244/255,244/255,1)
case mcontrol.const_error: return Qt.rgba(253/255,231/255,233/255,1)
}
return Qt.rgba(1,1,1,1)
}
}
Shadow {
radius: 4
}
radius: 4
border.width: 1
border.color: {
if(FluTheme.dark){
switch(_super.type){
case mcontrol.const_success: return Qt.rgba(56/255,61/255,27/255,1)
case mcontrol.const_warning: return Qt.rgba(66/255,53/255,25/255,1)
case mcontrol.const_info: return Qt.rgba(38/255,39/255,39/255,1)
case mcontrol.const_error: return Qt.rgba(67/255,39/255,38/255,1)
}
return Qt.rgba(1,1,1,1)
}else{
switch(_super.type){
case mcontrol.const_success: return Qt.rgba(210/255,232/255,208/255,1)
case mcontrol.const_warning: return Qt.rgba(240/255,230/255,194/255,1)
case mcontrol.const_info: return Qt.rgba(230/255,230/255,230/255,1)
case mcontrol.const_error: return Qt.rgba(238/255,217/255,219/255,1)
}
return Qt.rgba(1,1,1,1)
}
}
Quick.Row{
id:rowlayout
x:20
y:(parent.height - height) / 2
spacing: 10
Icon{
iconSource:{
switch(_super.type){
case mcontrol.const_success: return FluentIcons.CompletedSolid
case mcontrol.const_warning: return FluentIcons.InfoSolid
case mcontrol.const_info: return FluentIcons.InfoSolid
case mcontrol.const_error: return FluentIcons.StatusErrorFull
}FluentIcons.StatusErrorFull
return FluentIcons.FA_info_circle
}
iconSize:20
iconColor: {
if(FluTheme.dark){
switch(_super.type){
case mcontrol.const_success: return Qt.rgba(108/255,203/255,95/255,1)
case mcontrol.const_warning: return Qt.rgba(252/255,225/255,0/255,1)
case mcontrol.const_info: return FluTheme.primaryColor
case mcontrol.const_error: return Qt.rgba(255/255,153/255,164/255,1)
}
return Qt.rgba(1,1,1,1)
}else{
switch(_super.type){
case mcontrol.const_success: return Qt.rgba(15/255,123/255,15/255,1)
case mcontrol.const_warning: return Qt.rgba(157/255,93/255,0/255,1)
case mcontrol.const_info: return Qt.rgba(0/255,102/255,180/255,1)
case mcontrol.const_error: return Qt.rgba(196/255,43/255,28/255,1)
}
return Qt.rgba(1,1,1,1)
}
}
}
Quick.Column{
spacing: 5
Text{
text:_super.text
wrapMode: Text.WrapAnywhere
width: Math.min(implicitWidth,mcontrol.maxWidth)
}
Text{
text: _super.moremsg
visible: _super.moremsg
wrapMode : Text.WrapAnywhere
textColor: FluColors.Grey120
width: Math.min(implicitWidth,mcontrol.maxWidth)
}
}
IconButton{
id:btn_close
iconSource: FluentIcons.ChromeClose
iconSize: 10
verticalPadding: 0
horizontalPadding: 0
width: 30
height: 20
visible: _super.duration<=0
anchors.verticalCenter: parent.verticalCenter
iconColor: FluTheme.dark ? Qt.rgba(222/255,222/255,222/255,1) : Qt.rgba(97/255,97/255,97/255,1)
onClicked: _super.close()
}
}
}
}
function showSuccess(text,duration=1000,moremsg){
return mcontrol.create(mcontrol.const_success,text,duration,moremsg ? moremsg : "")
}
function showInfo(text,duration=1000,moremsg){
return mcontrol.create(mcontrol.const_info,text,duration,moremsg ? moremsg : "")
}
function showWarning(text,duration=1000,moremsg){
return mcontrol.create(mcontrol.const_warning,text,duration,moremsg ? moremsg : "")
}
function showError(text,duration=1000,moremsg){
return mcontrol.create(mcontrol.const_error,text,duration,moremsg ? moremsg : "")
}
function showCustom(itemcomponent,duration=1000){
return mcontrol.createCustom(itemcomponent,duration)
}
function clearAllInfo(){
if(mcontrol.screenLayout != null) {
mcontrol.screenLayout.destroy()
mcontrol.screenLayout = null
}
return true
}
}
import QtQuick as Quick
import QtQuick.Controls
Object {
property var root
property int layoutY: 75
id:control
Object{
id:mcontrol
property string const_success: "success"
property string const_info: "info"
property string const_warning: "warning"
property string const_error: "error"
property int maxWidth: 300
property var screenLayout: null
function create(type,text,duration,moremsg){
if(screenLayout){
var last = screenLayout.getLastloader()
if(last.type === type && last.text === text && moremsg === last.moremsg){
last.duration = duration
if (duration > 0) last.restart()
return last
}
}
initScreenLayout()
return contentComponent.createObject(screenLayout,{type:type,text:text,duration:duration,moremsg:moremsg,})
}
function createCustom(itemcomponent,duration){
initScreenLayout()
if(itemcomponent){
return contentComponent.createObject(screenLayout,{itemcomponent:itemcomponent,duration:duration})
}
}
function initScreenLayout(){
if(screenLayout == null){
screenLayout = screenlayoutComponent.createObject(root)
screenLayout.y = control.layoutY
screenLayout.z = 100000
}
}
Quick.Component{
id:screenlayoutComponent
Quick.Column{
parent: Overlay.overlay
z:999
spacing: 20
width: root.width
move: Quick.Transition {
Quick.NumberAnimation {
properties: "y"
easing.type: Easing.OutCubic
duration: FluTheme.animationEnabled ? 333 : 0
}
}
onChildrenChanged: if(children.length === 0) destroy()
function getLastloader(){
if(children.length > 0){
return children[children.length - 1]
}
return null
}
}
}
Quick.Component{
id:contentComponent
Quick.Item{
id:content
property int duration: 1500
property var itemcomponent
property string type
property string text
property string moremsg
width: parent.width
height: loader.height
function close(){
content.destroy()
}
function restart(){
delayTimer.restart()
}
Quick.Timer {
id:delayTimer
interval: duration
running: duration > 0
repeat: duration > 0
onTriggered: content.close()
}
Loader{
id:loader
x:(parent.width - width) / 2
property var _super: content
scale: item ? 1 : 0
asynchronous: true
Quick.Behavior on scale {
enabled: FluTheme.animationEnabled
Quick.NumberAnimation {
easing.type: Easing.OutCubic
duration: 167
}
}
sourceComponent:itemcomponent ? itemcomponent : mcontrol.fluent_sytle
}
}
}
property Quick.Component fluent_sytle: Quick.Rectangle{
width: rowlayout.width + (btn_close.visible ? 30 : 48)
height: rowlayout.height + 20
color: {
if(FluTheme.dark){
switch(_super.type){
case mcontrol.const_success: return Qt.rgba(57/255,61/255,27/255,1)
case mcontrol.const_warning: return Qt.rgba(67/255,53/255,25/255,1)
case mcontrol.const_info: return Qt.rgba(39/255,39/255,39/255,1)
case mcontrol.const_error: return Qt.rgba(68/255,39/255,38/255,1)
}
return Qt.rgba(1,1,1,1)
}else{
switch(_super.type){
case mcontrol.const_success: return Qt.rgba(223/255,246/255,221/255,1)
case mcontrol.const_warning: return Qt.rgba(255/255,244/255,206/255,1)
case mcontrol.const_info: return Qt.rgba(244/255,244/255,244/255,1)
case mcontrol.const_error: return Qt.rgba(253/255,231/255,233/255,1)
}
return Qt.rgba(1,1,1,1)
}
}
Shadow{
radius: 4
}
radius: 4
border.width: 1
border.color: {
if(FluTheme.dark){
switch(_super.type){
case mcontrol.const_success: return Qt.rgba(56/255,61/255,27/255,1)
case mcontrol.const_warning: return Qt.rgba(66/255,53/255,25/255,1)
case mcontrol.const_info: return Qt.rgba(38/255,39/255,39/255,1)
case mcontrol.const_error: return Qt.rgba(67/255,39/255,38/255,1)
}
return Qt.rgba(1,1,1,1)
}else{
switch(_super.type){
case mcontrol.const_success: return Qt.rgba(210/255,232/255,208/255,1)
case mcontrol.const_warning: return Qt.rgba(240/255,230/255,194/255,1)
case mcontrol.const_info: return Qt.rgba(230/255,230/255,230/255,1)
case mcontrol.const_error: return Qt.rgba(238/255,217/255,219/255,1)
}
return Qt.rgba(1,1,1,1)
}
}
Quick.Row{
id:rowlayout
x:20
y:(parent.height - height) / 2
spacing: 10
Icon{
iconSource:{
switch(_super.type){
case mcontrol.const_success: return FluentIcons.CompletedSolid
case mcontrol.const_warning: return FluentIcons.InfoSolid
case mcontrol.const_info: return FluentIcons.InfoSolid
case mcontrol.const_error: return FluentIcons.StatusErrorFull
}FluentIcons.StatusErrorFull
return FluentIcons.FA_info_circle
}
iconSize:20
iconColor: {
if(FluTheme.dark){
switch(_super.type){
case mcontrol.const_success: return Qt.rgba(108/255,203/255,95/255,1)
case mcontrol.const_warning: return Qt.rgba(252/255,225/255,0/255,1)
case mcontrol.const_info: return FluTheme.primaryColor
case mcontrol.const_error: return Qt.rgba(255/255,153/255,164/255,1)
}
return Qt.rgba(1,1,1,1)
}else{
switch(_super.type){
case mcontrol.const_success: return Qt.rgba(15/255,123/255,15/255,1)
case mcontrol.const_warning: return Qt.rgba(157/255,93/255,0/255,1)
case mcontrol.const_info: return Qt.rgba(0/255,102/255,180/255,1)
case mcontrol.const_error: return Qt.rgba(196/255,43/255,28/255,1)
}
return Qt.rgba(1,1,1,1)
}
}
}
Quick.Column{
spacing: 5
Text{
text:_super.text
wrapMode: Text.WrapAnywhere
width: Math.min(implicitWidth,mcontrol.maxWidth)
}
Text{
text: _super.moremsg
visible: _super.moremsg
wrapMode : Text.WrapAnywhere
textColor: FluColors.Grey120
width: Math.min(implicitWidth,mcontrol.maxWidth)
}
}
IconButton{
id:btn_close
iconSource: FluentIcons.ChromeClose
iconSize: 10
verticalPadding: 0
horizontalPadding: 0
width: 30
height: 20
visible: _super.duration<=0
anchors.verticalCenter: parent.verticalCenter
iconColor: FluTheme.dark ? Qt.rgba(222/255,222/255,222/255,1) : Qt.rgba(97/255,97/255,97/255,1)
onClicked: _super.close()
}
}
}
}
function showSuccess(text,duration=1000,moremsg){
return mcontrol.create(mcontrol.const_success,text,duration,moremsg ? moremsg : "")
}
function showInfo(text,duration=1000,moremsg){
return mcontrol.create(mcontrol.const_info,text,duration,moremsg ? moremsg : "")
}
function showWarning(text,duration=1000,moremsg){
return mcontrol.create(mcontrol.const_warning,text,duration,moremsg ? moremsg : "")
}
function showError(text,duration=1000,moremsg){
return mcontrol.create(mcontrol.const_error,text,duration,moremsg ? moremsg : "")
}
function showCustom(itemcomponent,duration=1000){
return mcontrol.createCustom(itemcomponent,duration)
}
function clearAllInfo(){
if(mcontrol.screenLayout != null) {
mcontrol.screenLayout.destroy()
mcontrol.screenLayout = null
}
return true
}
}

5
Fluent/qml/Loader.qml Normal file
View File

@ -0,0 +1,5 @@
import QtQuick
Loader {
Component.onDestruction: sourceComponent = undefined
}

View File

@ -1,7 +1,7 @@
import QtQuick
QtObject {
id:root
default property list<QtObject> children
}
import QtQuick
import QtQuick.Controls
QtObject {
default property list<QtObject> children
id:control
}

54
Fluent/qml/Popup.qml Normal file
View File

@ -0,0 +1,54 @@
import QtQuick as Quick
import QtQuick.Layouts
import QtQuick.Controls as Controls
import QtQuick.Window
import Fluent
Controls.Popup {
id: control
padding: 0
modal:true
parent: Controls.Overlay.overlay
x: Math.round((d.parentWidth - width) / 2)
y: Math.round((d.parentHeight - height) / 2)
closePolicy: Popup.NoAutoClose
enter: Transition {
NumberAnimation {
property: "opacity"
duration: Theme.animationEnabled ? 83 : 0
from:0
to:1
}
}
height:Math.min(implicitHeight,d.parentHeight)
exit:Transition {
NumberAnimation {
property: "opacity"
duration: Theme.animationEnabled ? 83 : 0
from:1
to:0
}
}
background: Rectangle{
radius: [5,5,5,5]
color: Theme.dark ? Qt.rgba(43/255,43/255,43/255,1) : Qt.rgba(1,1,1,1)
Shadow{
radius: 5
}
}
QtObject{
id:d
property int parentHeight: {
if(control.parent){
return control.parent.height
}
return control.height
}
property int parentWidth: {
if(control.parent){
return control.parent.width
}
return control.width
}
}
}

View File

@ -0,0 +1,92 @@
import QtQuick as Quick
import QtQuick.Controls
import Fluent
ProgressBar{
property int duration: 2000
property real strokeWidth: 6
property bool progressVisible: false
property Quick.color color: FluTheme.primaryColor
property Quick.color backgroundColor : FluTheme.dark ? Qt.rgba(99/255,99/255,99/255,1) : Qt.rgba(214/255,214/255,214/255,1)
id:control
indeterminate : true
clip: true
background: Quick.Rectangle {
implicitWidth: 56
implicitHeight: 56
radius: control.width/2
color:"transparent"
border.color: control.backgroundColor
border.width: control.strokeWidth
}
onIndeterminateChanged:{
canvas.requestPaint()
}
Quick.QtObject{
id:d
property real _radius: control.width/2-control.strokeWidth/2
property real _progress: control.indeterminate ? 0.0 : control.visualPosition
on_ProgressChanged: {
canvas.requestPaint()
}
}
Quick.Connections{
target: FluTheme
function onDarkChanged(){
canvas.requestPaint()
}
}
contentItem: Quick.Item {
id:layout_item
Quick.Canvas {
id:canvas
anchors.fill: parent
antialiasing: true
renderTarget: Canvas.Image
property real startAngle: 0
property real sweepAngle: 0
Quick.SequentialAnimation on startAngle {
loops: Animation.Infinite
running: control.visible && control.indeterminate
Quick.PropertyAnimation { from: 0; to: 450; duration: control.duration/2 }
Quick.PropertyAnimation { from: 450; to: 1080; duration: control.duration/2 }
}
Quick.SequentialAnimation on sweepAngle {
loops: Animation.Infinite
running: control.visible && control.indeterminate
Quick.PropertyAnimation { from: 0; to: 180; duration: control.duration/2 }
Quick.PropertyAnimation { from: 180; to: 0; duration: control.duration/2 }
}
onStartAngleChanged: {
requestPaint()
}
onPaint: {
var ctx = canvas.getContext("2d")
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.save()
ctx.lineWidth = control.strokeWidth
ctx.strokeStyle = control.color
ctx.lineCap = "round"
ctx.beginPath()
if(control.indeterminate){
ctx.arc(width/2, height/2, d._radius , Math.PI * (startAngle - 90) / 180, Math.PI * (startAngle - 90 + sweepAngle) / 180)
}else{
ctx.arc(width/2, height/2, d._radius , -0.5 * Math.PI , -0.5 * Math.PI + d._progress * 2 * Math.PI)
}
ctx.stroke()
ctx.closePath()
ctx.restore()
}
}
}
Text{
text:(control.visualPosition * 100).toFixed(0) + "%"
visible: {
if(control.indeterminate){
return false
}
return control.progressVisible
}
anchors.centerIn: parent
}
}

View File

@ -1,12 +1,71 @@
pragma Singleton
import QtQml
QtObject {
property var windows: []
function addWindow(window){
if(!window.transientParent){
windows.push(window)
}
}
}
pragma Singleton
import QtQuick
QtObject {
property var routes : ({})
property var windows: []
function addWindow(window){
if(!window.transientParent){
windows.push(window)
}
}
function removeWindow(win) {
if(!win.transientParent){
var index = windows.indexOf(win)
if (index !== -1) {
windows.splice(index, 1)
win.deleteLater()
}
}
}
function exit(retCode){
for(var i =0 ;i< windows.length; i++){
var win = windows[i]
win.deleteLater()
}
windows = []
Qt.exit(retCode)
}
function navigate(route,argument={},windowRegister = undefined){
if(!routes.hasOwnProperty(route)){
console.error("Not Found Route",route)
return
}
var windowComponent = Qt.createComponent(routes[route])
if (windowComponent.status !== Component.Ready) {
console.error(windowComponent.errorString())
return
}
var properties = {}
properties._route = route
if(windowRegister){
properties._windowRegister = windowRegister
}
properties.argument = argument
var win = undefined
for(var i =0 ;i< windows.length; i++){
var item = windows[i]
if(route === item._route){
win = item
break
}
}
if(win){
var launchMode = win.launchMode
if(launchMode === 1){
win.argument = argument
win.show()
win.raise()
win.requestActivate()
return
}else if(launchMode === 2){
win.close()
}
}
win = windowComponent.createObject(null,properties)
if(windowRegister){
windowRegister._to = win
}
}
}

187
Fluent/qml/ScrollBar.qml Normal file
View File

@ -0,0 +1,187 @@
import QtQuick
import QtQuick.Controls.impl
import QtQuick.Templates as T
import Fluent
T.ScrollBar {
id: control
property color color : Theme.dark ? Qt.rgba(159/255,159/255,159/255,1) : Qt.rgba(138/255,138/255,138/255,1)
property color pressedColor: Theme.dark ? Qt.darker(color,1.2) : Qt.lighter(color,1.2)
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding)
visible: control.policy !== T.ScrollBar.AlwaysOff
minimumSize: Math.max(orientation === Qt.Horizontal ? height / width : width / height,0.3)
QtObject{
id:d
property int minLine : 2
property int maxLine : 6
}
z: horizontal? 10 : 20
verticalPadding : vertical ? 15 : 3
horizontalPadding : horizontal ? 15 : 3
background: Rectangle{
id:back_rect
radius: 5
color:Theme.dark ? Qt.rgba(44/255,44/255,44/255,1) : Qt.rgba(255/255,255/255,255/255,1)
opacity:{
if(vertical){
return d.maxLine === Number(rect_bar.width)
}
return d.maxLine === Number(rect_bar.height)
}
Behavior on opacity {
NumberAnimation{
duration: 50
}
}
}
IconButton{
width: 12
height: 12
iconSize: 8
verticalPadding: 0
horizontalPadding: 0
visible: control.horizontal
opacity: back_rect.opacity
anchors{
left: parent.left
leftMargin: 2
verticalCenter: parent.verticalCenter
}
iconColor: control.color
iconSource: Icons.CaretLeftSolid8
onClicked: {
control.decrease()
}
}
IconButton{
width: 12
height: 12
iconSize: 8
verticalPadding: 0
horizontalPadding: 0
iconColor: control.color
opacity: back_rect.opacity
anchors{
right: parent.right
rightMargin: 2
verticalCenter: parent.verticalCenter
}
visible: control.horizontal
iconSource: Icons.CaretRightSolid8
onClicked: {
control.increase()
}
}
IconButton{
width: 12
height: 12
iconSize: 8
verticalPadding: 0
horizontalPadding: 0
iconColor: control.color
opacity: back_rect.opacity
anchors{
top: parent.top
topMargin: 2
horizontalCenter: parent.horizontalCenter
}
visible: control.vertical
iconSource: Icons.CaretUpSolid8
onClicked: {
control.decrease()
}
}
IconButton{
width: 12
height: 12
iconSize: 8
verticalPadding: 0
horizontalPadding: 0
iconColor: control.color
opacity: back_rect.opacity
anchors{
bottom: parent.bottom
bottomMargin: 2
horizontalCenter: parent.horizontalCenter
}
visible: control.vertical
iconSource: Icons.CaretDownSolid8
onClicked: {
control.increase()
}
}
contentItem: Item {
property bool collapsed: (control.policy === T.ScrollBar.AlwaysOn || (control.active && control.size < 1.0))
implicitWidth: control.interactive ? d.maxLine : d.minLine
implicitHeight: control.interactive ? d.maxLine : d.minLine
Rectangle{
id:rect_bar
width: vertical ? d.minLine : parent.width
height: horizontal ? d.minLine : parent.height
color:{
if(control.pressed){
return control.pressedColor
}
return control .color
}
anchors{
right: vertical ? parent.right : undefined
bottom: horizontal ? parent.bottom : undefined
}
radius: width / 2
visible: control.size < 1.0
}
states: [
State{
name:"show"
when: contentItem.collapsed
PropertyChanges {
target: rect_bar
width: vertical ? d.maxLine : parent.width
height: horizontal ? d.maxLine : parent.height
}
}
,State{
name:"hide"
when: !contentItem.collapsed
PropertyChanges {
target: rect_bar
width: vertical ? d.minLine : parent.width
height: horizontal ? d.minLine : parent.height
}
}
]
transitions:[
Transition {
to: "hide"
SequentialAnimation {
PauseAnimation { duration: 450 }
NumberAnimation {
target: rect_bar
properties: vertical ? "width" : "height"
duration: 167
easing.type: Easing.OutCubic
}
}
}
,Transition {
to: "show"
SequentialAnimation {
PauseAnimation { duration: 150 }
NumberAnimation {
target: rect_bar
properties: vertical ? "width" : "height"
duration: 167
easing.type: Easing.OutCubic
}
}
}
]
}
}

View File

@ -1,14 +1,15 @@
import QtQuick
import QtQuick as Quick
import Fluent
Item {
property color color: FluTheme.dark ? "#000000" : "#999999"
Quick.Item {
property Quick.color color: Theme.dark ? "#000000" : "#999999"
property int elevation: 5
property int radius: 4
id:control
anchors.fill: parent
Repeater{
Quick.Repeater{
model: elevation
Rectangle{
Quick.Rectangle{
anchors.fill: parent
color: "#00000000"
opacity: 0.01 * (elevation-index+1)

View File

@ -1,11 +1,10 @@
import QtQuick as Quick
import Fluent
Quick.Text {
property Quick.color textColor: FluTheme.fontPrimaryColor
property Quick.color textColor: Theme.fontPrimaryColor
id:text
color: textColor
renderType: FluTheme.nativeText ? Text.NativeRendering : Text.QtRendering
font: FluTextStyle.Body
renderType: Theme.nativeText ? Text.NativeRendering : Text.QtRendering
font: TextStyle.Body
}

31
Fluent/qml/Tooltip.qml Normal file
View File

@ -0,0 +1,31 @@
import QtQuick as Quick
import QtQuick.Controls.impl
import QtQuick.Templates as T
import Fluent
T.ToolTip {
id: control
x: parent ? (parent.width - implicitWidth) / 2 : 0
y: -implicitHeight - 3
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
contentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
contentHeight + topPadding + bottomPadding)
margins: 6
padding: 6
font: TextStyle.Body
closePolicy: T.Popup.CloseOnEscape | T.Popup.CloseOnPressOutsideParent | T.Popup.CloseOnReleaseOutsideParent
contentItem: Text {
text: control.text
font: control.font
wrapMode: Text.Wrap
}
background: Quick.Rectangle {
color: Theme.dark ? Qt.rgba(50/255,49/255,48/255,1) : Qt.rgba(1,1,1,1)
radius: 3
Shadow{
radius: 3
}
}
}

View File

@ -1,186 +1,387 @@
import QtQuick as Quick
import Fluent
Quick.Window {
id: root
property string windowIcon: App.windowIcon
property bool showStayTop: false
property bool showMaximize: true
property bool showMinimize: true
property bool showClose: true
property bool showDark: false
property bool fixSize: false
property bool stayTop: false
property int __margins: 0
property var background : com_background
property Quick.color backgroundColor: {
if(active){
return Theme.windowActiveBackgroundColor
}
return Theme.windowBackgroundColor
}
property Quick.Item appBar: AppBar {
title: root.title
height: 30
showDark: root.showDark
showClose: root.showClose
showMinimize: root.showMinimize
showMaximize: root.showMaximize
showStayTop: root.showStayTop
icon: root.windowIcon
}
Frameless {
id: frameless
appBar: root.appBar
maximizeButton: appBar.buttonMaximize
fixSize: root.fixSize
topmost: root.stayTop
disabled: App.useSystemAppBar
Quick.Component.onCompleted: {
frameless.setHitTestVisible(appBar.layoutMacosButtons)
frameless.setHitTestVisible(appBar.layoutStandardbuttons)
}
Quick.Component.onDestruction: {
frameless.onDestruction()
}
}
Quick.Component.onCompleted: {
Router.addWindow(root)
}
Quick.Component {
id:com_app_bar
Quick.Item{
data: root.appBar
Quick.Component.onCompleted: {
root.appBar.width = Qt.binding(function(){
return this.parent.width
})
}
}
}
Quick.Item{
id: layout_container
anchors.fill: parent
anchors.margins: root.__margins
Quick.Loader{
anchors.fill: parent
sourceComponent: background
Quick.Component.onDestruction: sourceComponent = undefined
}
Quick.Loader{
id:loader_app_bar
anchors {
top: parent.top
left: parent.left
right: parent.right
}
height: {
if(root.useSystemAppBar){
return 0
}
return root.fitsAppBarWindows ? 0 : root.appBar.height
}
sourceComponent: root.useSystemAppBar ? undefined : com_app_bar
Quick.Component.onDestruction: sourceComponent = undefined
}
Quick.Item{
id:layout_content
anchors{
top: loader_app_bar.bottom
left: parent.left
right: parent.right
bottom: parent.bottom
}
clip: true
}
Quick.Loader{
property string loadingText
property bool cancel: false
id:loader_loading
anchors.fill: parent
Quick.Component.onDestruction: sourceComponent = undefined
}
InfoBar{
id:info_bar
root: layout_container
}
Quick.Loader{
id:loader_border
anchors.fill: parent
sourceComponent: {
if(root.useSystemAppBar || Utilities.isWin() || root.visibility === Window.Maximized || root.visibility === Window.FullScreen){
return undefined
}
return com_border
}
Quick.Component.onDestruction: sourceComponent = undefined
}
}
Quick.Component {
id:com_background
Quick.Item{
Rectangle{
anchors.fill: parent
color: root.backgroundColor
}
Quick.Image{
id:img_back
visible: false
cache: false
fillMode: Quick.Image.PreserveAspectCrop
asynchronous: true
Quick.Component.onCompleted: {
img_back.updateLayout()
source = Utilities.getUrlByFilePath(Theme.desktopImagePath)
}
Quick.Connections{
target: root
function onScreenChanged(){
img_back.updateLayout()
}
}
function updateLayout(){
var geometry = Utilities.desktopAvailableGeometry(root)
img_back.width = geometry.width
img_back.height = geometry.height
img_back.sourceSize = Qt.size(img_back.width,img_back.height)
}
Quick.Connections{
target: Theme
function onDesktopImagePathChanged(){
timer_update_image.restart()
}
function onBlurBehindWindowEnabledChanged(){
if(Theme.blurBehindWindowEnabled){
img_back.source = Utilities.getUrlByFilePath(Theme.desktopImagePath)
}else{
img_back.source = ""
}
}
}
Quick.Timer{
id:timer_update_image
interval: 150
onTriggered: {
img_back.source = ""
img_back.source = Utilities.getUrlByFilePath(Theme.desktopImagePath)
}
}
}
Acrylic{
anchors.fill: parent
target: img_back
tintOpacity: Theme.dark ? 0.80 : 0.75
blurRadius: 64
visible: root.active && Theme.blurBehindWindowEnabled
tintColor: Theme.dark ? Qt.rgba(0, 0, 0, 1) : Qt.rgba(1, 1, 1, 1)
targetRect: Qt.rect(root.x-root.screen.virtualX,root.y-root.screen.virtualY,root.width,root.height)
}
}
}
}
import QtQuick as Quick
import QtQuick.Controls
import QtQuick.Layouts
import Fluent
Quick.Window {
id: window
default property alias contentData : layout_content.data
property string windowIcon: App.windowIcon
property int launchMode: WindowType.Standard
property var argument:({})
property var background : com_background
property bool fixSize: false
property Quick.Component loadingItem: com_loading
property bool fitsAppBarWindows: false
property var tintOpacity: Theme.dark ? 0.80 : 0.75
property int blurRadius: 60
property alias effect: frameless.effect
readonly property alias effective: frameless.effective
readonly property var availableEffects: frameless.availableEffects
property Quick.Item appBar: AppBar {
title: window.title
height: 30
showDark: window.showDark
showClose: window.showClose
showMinimize: window.showMinimize
showMaximize: window.showMaximize
showStayTop: window.showStayTop
icon: window.windowIcon
}
property Quick.color backgroundColor: {
if(frameless.effective && active){
var backcolor
if(frameless.effect==="dwm-blur"){
backcolor = Utilities.withOpacity(Theme.windowActiveBackgroundColor, window.tintOpacity)
}else{
backcolor = "transparent"
}
return backcolor
}
if(active){
return Theme.windowActiveBackgroundColor
}
return Theme.windowBackgroundColor
}
property bool stayTop: false
property bool showDark: false
property bool showClose: true
property bool showMinimize: true
property bool showMaximize: true
property bool showStayTop: false
property bool autoMaximize: false
property bool autoVisible: true
property bool autoCenter: true
property bool autoDestroy: true
property bool useSystemAppBar
property int __margins: 0
property Quick.color resizeBorderColor: {
if(window.active){
return Theme.dark ? Qt.rgba(51/255,51/255,51/255,1) : Qt.rgba(110/255,110/255,110/255,1)
}
return Theme.dark ? Qt.rgba(61/255,61/255,61/255,1) : Qt.rgba(167/255,167/255,167/255,1)
}
property int resizeBorderWidth: 1
property var closeListener: function(event){
if(autoDestroy){
Router.removeWindow(window)
}else{
window.visibility = Window.Hidden
event.accepted = false
}
}
signal initArgument(var argument)
signal lazyLoad()
property var _windowRegister
property string _route
property bool _hideShadow: false
color: Utilities.isSoftware() ? window.backgroundColor : "transparent"
Quick.Component.onCompleted: {
Router.addWindow(window)
useSystemAppBar = App.useSystemAppBar
if(!useSystemAppBar && autoCenter){
moveWindowToDesktopCenter()
}
fixWindowSize()
initArgument(argument)
if(window.autoVisible){
if(window.autoMaximize){
window.visibility = Window.Maximized
}else{
window.show()
}
}
}
onVisibleChanged: {
if(visible && d.isLazyInit){
window.lazyLoad()
d.isLazyInit = false
}
}
Quick.QtObject{
id:d
property bool isLazyInit: true
}
Quick.Connections{
target: window
function onClosing(event){closeListener(event)}
}
Frameless{
id: frameless
appBar: window.appBar
maximizeButton: appBar.buttonMaximize
fixSize: window.fixSize
topmost: window.stayTop
disabled: App.useSystemAppBar
Quick.Component.onCompleted: {
frameless.setHitTestVisible(appBar.layoutMacosButtons)
frameless.setHitTestVisible(appBar.layoutStandardbuttons)
}
Quick.Component.onDestruction: {
frameless.onDestruction()
}
onEffectiveChanged: {
if(effective){
Theme.blurBehindWindowEnabled = false
}
}
}
Quick.Component{
id:com_background
Quick.Item{
Quick.Rectangle{
anchors.fill: parent
color: window.backgroundColor
}
Quick.Image{
id:img_back
visible: false
cache: false
fillMode: Quick.Image.PreserveAspectCrop
asynchronous: true
Quick.Component.onCompleted: {
img_back.updateLayout()
source = Utilities.getUrlByFilePath(Theme.desktopImagePath)
}
Quick.Connections{
target: window
function onScreenChanged(){
img_back.updateLayout()
}
}
function updateLayout(){
var geometry = Utilities.desktopAvailableGeometry(window)
img_back.width = geometry.width
img_back.height = geometry.height
img_back.sourceSize = Qt.size(img_back.width,img_back.height)
}
Quick.Connections{
target: Theme
function onDesktopImagePathChanged(){
timer_update_image.restart()
}
function onBlurBehindWindowEnabledChanged(){
if(Theme.blurBehindWindowEnabled){
img_back.source = Utilities.getUrlByFilePath(Theme.desktopImagePath)
}else{
img_back.source = ""
}
}
}
Quick.Timer{
id:timer_update_image
interval: 150
onTriggered: {
img_back.source = ""
img_back.source = Utilities.getUrlByFilePath(Theme.desktopImagePath)
}
}
}
Acrylic{
anchors.fill: parent
target: img_back
tintOpacity: window.tintOpacity
blurRadius: window.blurRadius
visible: window.active && Theme.blurBehindWindowEnabled
tintColor: Theme.dark ? Qt.rgba(0, 0, 0, 1) : Qt.rgba(1, 1, 1, 1)
targetRect: Qt.rect(window.x-window.screen.virtualX,window.y-window.screen.virtualY,window.width,window.height)
}
}
}
Quick.Component{
id:com_app_bar
Quick.Item{
data: window.appBar
Quick.Component.onCompleted: {
window.appBar.width = Qt.binding(function(){
return this.parent.width
})
}
}
}
Quick.Component{
id:com_loading
Popup{
id:popup_loading
focus: true
width: window.width
height: window.height
anchors.centerIn: Overlay.overlay
closePolicy: {
if(cancel){
return Popup.CloseOnEscape | Popup.CloseOnPressOutside
}
return Popup.NoAutoClose
}
Overlay.modal: Quick.Item {}
onVisibleChanged: {
if(!visible){
loader_loading.sourceComponent = undefined
}
}
padding: 0
opacity: 0
visible:true
Quick.Behavior on opacity {
Quick.SequentialAnimation {
Quick.PauseAnimation {
duration: 83
}
Quick.NumberAnimation{
duration: 167
}
}
}
Quick.Component.onCompleted: {
opacity = 1
}
background: Quick.Rectangle{
color:"#44000000"
}
contentItem: Quick.Item{
Quick.MouseArea{
anchors.fill: parent
onClicked: {
if (cancel){
popup_loading.visible = false
}
}
}
ColumnLayout{
spacing: 8
anchors.centerIn: parent
ProgressRing{
Layout.alignment: Qt.AlignHCenter
}
Text{
text:loadingText
Layout.alignment: Qt.AlignHCenter
}
}
}
}
}
Quick.Component{
id:com_border
Quick.Rectangle{
color:"transparent"
border.width: window.resizeBorderWidth
border.color: window.resizeBorderColor
}
}
Quick.Item{
id: layout_container
anchors.fill: parent
anchors.margins: window.__margins
Loader{
anchors.fill: parent
sourceComponent: background
}
Loader{
id:loader_app_bar
anchors {
top: parent.top
left: parent.left
right: parent.right
}
height: {
if(window.useSystemAppBar){
return 0
}
return window.fitsAppBarWindows ? 0 : window.appBar.height
}
sourceComponent: window.useSystemAppBar ? undefined : com_app_bar
}
Quick.Item{
id: layout_content
anchors{
top: loader_app_bar.bottom
left: parent.left
right: parent.right
bottom: parent.bottom
}
clip: true
}
Loader{
property string loadingText
property bool cancel: false
id:loader_loading
anchors.fill: parent
}
InfoBar{
id:info_bar
root: layout_container
}
Loader{
id:loader_border
anchors.fill: parent
sourceComponent: {
if(window.useSystemAppBar || Utilities.isWin() || window.visibility === Window.Maximized || window.visibility === Window.FullScreen){
return undefined
}
return com_border
}
}
}
function hideLoading(){
loader_loading.sourceComponent = undefined
}
function showSuccess(text,duration,moremsg){
return info_bar.showSuccess(text,duration,moremsg)
}
function showInfo(text,duration,moremsg){
return info_bar.showInfo(text,duration,moremsg)
}
function showWarning(text,duration,moremsg){
return info_bar.showWarning(text,duration,moremsg)
}
function showError(text,duration,moremsg){
return info_bar.showError(text,duration,moremsg)
}
function clearAllInfo(){
return info_bar.clearAllInfo()
}
function moveWindowToDesktopCenter(){
var availableGeometry = Utilities.desktopAvailableGeometry(window)
window.setGeometry((availableGeometry.width-window.width)/2+Quick.Screen.virtualX,(availableGeometry.height-window.height)/2+Quick.Screen.virtualY,window.width,window.height)
}
function fixWindowSize(){
if(fixSize){
window.maximumWidth = window.width
window.maximumHeight = window.height
window.minimumWidth = window.width
window.minimumHeight = window.height
}
}
function setResult(data){
if(_windowRegister){
_windowRegister.setResult(data)
}
}
function showMaximized(){
frameless.showMaximized()
}
function showMinimized(){
frameless.showMinimized()
}
function showNormal(){
frameless.showNormal()
}
function showLoading(text = "",cancel = true){
if(text===""){
text = qsTr("Loading...")
}
loader_loading.loadingText = text
loader_loading.cancel = cancel
loader_loading.sourceComponent = com_loading
}
function setHitTestVisible(val){
frameless.setHitTestVisible(val)
}
function deleteLater(){
Utilities.deleteLater(window)
}
function containerItem(){
return layout_container
}
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB