mirror of
https://github.com/zhuzichu520/FluentUI.git
synced 2024-11-22 10:40:10 +08:00
update
This commit is contained in:
parent
44acdbcf7f
commit
0ab315e258
@ -2190,6 +2190,11 @@ Some contents...</source>
|
||||
<source>Open Animation</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qml/page/T_Theme.qml" line="123"/>
|
||||
<source>Open Blur Window</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>T_TimePicker</name>
|
||||
|
@ -2280,6 +2280,11 @@ Some contents...</source>
|
||||
<source>Open Animation</source>
|
||||
<translation type="unfinished">开启动画</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qml/page/T_Theme.qml" line="123"/>
|
||||
<source>Open Blur Window</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>T_TimePicker</name>
|
||||
|
@ -13,7 +13,7 @@ FluScrollablePage{
|
||||
|
||||
FluFrame{
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 340
|
||||
Layout.preferredHeight: 400
|
||||
padding: 10
|
||||
|
||||
ColumnLayout{
|
||||
@ -119,6 +119,17 @@ FluScrollablePage{
|
||||
FluTheme.animationEnabled = !FluTheme.animationEnabled
|
||||
}
|
||||
}
|
||||
FluText{
|
||||
text: qsTr("Open Blur Window")
|
||||
Layout.topMargin: 20
|
||||
}
|
||||
FluToggleSwitch{
|
||||
Layout.topMargin: 5
|
||||
checked: FluTheme.blurBehindWindowEnabled
|
||||
onClicked: {
|
||||
FluTheme.blurBehindWindowEnabled = !FluTheme.blurBehindWindowEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CodeExpander{
|
||||
|
@ -2,9 +2,11 @@
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QPalette>
|
||||
#include <QImage>
|
||||
#include "Def.h"
|
||||
#include "FluentIconDef.h"
|
||||
#include "FluColors.h"
|
||||
#include "FluTools.h"
|
||||
|
||||
bool systemDark() {
|
||||
QPalette palette = QGuiApplication::palette();
|
||||
@ -18,13 +20,20 @@ FluTheme::FluTheme(QObject *parent) : QObject{parent} {
|
||||
_nativeText = false;
|
||||
_animationEnabled = true;
|
||||
_systemDark = systemDark();
|
||||
_desktopImagePath = "";
|
||||
_blurBehindWindowEnabled = false;
|
||||
QGuiApplication::instance()->installEventFilter(this);
|
||||
refreshColors();
|
||||
updateDesktopImage();
|
||||
connect(this, &FluTheme::darkModeChanged, this, [=] {
|
||||
Q_EMIT darkChanged();
|
||||
});
|
||||
connect(this, &FluTheme::darkChanged, this, [=] { refreshColors(); });
|
||||
connect(this, &FluTheme::accentColorChanged, this, [=] { refreshColors(); });
|
||||
refreshColors();
|
||||
connect(&_watcher, &QFileSystemWatcher::fileChanged, this, [=](const QString &path){
|
||||
Q_EMIT desktopImagePathChanged();
|
||||
});
|
||||
startTimer(500);
|
||||
}
|
||||
|
||||
void FluTheme::refreshColors() {
|
||||
@ -38,6 +47,8 @@ void FluTheme::refreshColors() {
|
||||
fontSecondaryColor(isDark ? QColor(222, 222, 222, 255) : QColor(102, 102, 102, 255));
|
||||
fontTertiaryColor(isDark ? QColor(200, 200, 200, 255) : QColor(153, 153, 153, 255));
|
||||
itemNormalColor(isDark ? QColor(255, 255, 255, 0) : QColor(0, 0, 0, 0));
|
||||
frameColor(isDark ? QColor(255, 255, 255, qRound(255 * 0.12)) : QColor(0, 0, 0, qRound(255 * 0.09)));
|
||||
frameActiveColor(isDark ? QColor(32, 32, 32, qRound(255 * 0.8)) : QColor(255, 255, 255, qRound(255 * 0.6)));
|
||||
itemHoverColor(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)));
|
||||
@ -78,3 +89,19 @@ bool FluTheme::dark() const {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void FluTheme::updateDesktopImage(){
|
||||
auto path = FluTools::getInstance()->getWallpaperFilePath();
|
||||
if(_desktopImagePath != path){
|
||||
if(!_desktopImagePath.isEmpty()){
|
||||
_watcher.removePath(_desktopImagePath);
|
||||
}
|
||||
desktopImagePath(path);
|
||||
_watcher.addPath(path);
|
||||
}
|
||||
}
|
||||
|
||||
void FluTheme::timerEvent(QTimerEvent *event)
|
||||
{
|
||||
updateDesktopImage();
|
||||
}
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QColor>
|
||||
#include <QTimer>
|
||||
#include <QFileSystemWatcher>
|
||||
#include "FluAccentColor.h"
|
||||
#include "stdafx.h"
|
||||
#include "singleton.h"
|
||||
@ -15,7 +17,7 @@
|
||||
*/
|
||||
class FluTheme : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool dark READ dark NOTIFY darkChanged)
|
||||
Q_PROPERTY(bool dark READ dark NOTIFY darkChanged)
|
||||
Q_PROPERTY_AUTO_P(FluAccentColor*, accentColor);
|
||||
Q_PROPERTY_AUTO(QColor, primaryColor);
|
||||
Q_PROPERTY_AUTO(QColor, backgroundColor);
|
||||
@ -26,12 +28,16 @@ Q_PROPERTY_AUTO(QColor, fontPrimaryColor);
|
||||
Q_PROPERTY_AUTO(QColor, fontSecondaryColor);
|
||||
Q_PROPERTY_AUTO(QColor, fontTertiaryColor);
|
||||
Q_PROPERTY_AUTO(QColor, itemNormalColor);
|
||||
Q_PROPERTY_AUTO(QColor, frameColor);
|
||||
Q_PROPERTY_AUTO(QColor, frameActiveColor);
|
||||
Q_PROPERTY_AUTO(QColor, itemHoverColor);
|
||||
Q_PROPERTY_AUTO(QColor, itemPressColor);
|
||||
Q_PROPERTY_AUTO(QColor, itemCheckColor);
|
||||
Q_PROPERTY_AUTO(QString, desktopImagePath);
|
||||
Q_PROPERTY_AUTO(int, darkMode);
|
||||
Q_PROPERTY_AUTO(bool, nativeText);
|
||||
Q_PROPERTY_AUTO(bool, animationEnabled);
|
||||
Q_PROPERTY_AUTO(bool, blurBehindWindowEnabled);
|
||||
QML_NAMED_ELEMENT(FluTheme)
|
||||
QML_SINGLETON
|
||||
|
||||
@ -42,6 +48,14 @@ private:
|
||||
|
||||
void refreshColors();
|
||||
|
||||
void updateBackgroundMainColor();
|
||||
|
||||
protected:
|
||||
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
|
||||
void updateDesktopImage();
|
||||
|
||||
public:
|
||||
SINGLETON(FluTheme)
|
||||
|
||||
@ -55,6 +69,7 @@ SINGLETON(FluTheme)
|
||||
|
||||
private:
|
||||
bool _systemDark;
|
||||
QFileSystemWatcher _watcher;
|
||||
};
|
||||
|
||||
#endif // FLUTHEME_H
|
||||
|
@ -16,6 +16,14 @@
|
||||
#include <QDateTime>
|
||||
#include <QSettings>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#pragma comment (lib, "user32.lib")
|
||||
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
#endif
|
||||
|
||||
FluTools::FluTools(QObject *parent) : QObject{parent} {
|
||||
|
||||
}
|
||||
@ -244,3 +252,32 @@ bool FluTools::isWindows10OrGreater() {
|
||||
QRect FluTools::desktopAvailableGeometry(QQuickWindow *window) {
|
||||
return window->screen()->availableGeometry();
|
||||
}
|
||||
|
||||
QString FluTools::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);
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
QColor FluTools::imageMainColor(const QImage& image, double bright) {
|
||||
int step = 20;
|
||||
int t = 0;
|
||||
int r = 0, g = 0, b = 0;
|
||||
for (int i = 0; i < image.width(); i += step) {
|
||||
for (int j = 0; j < image.height(); j += step) {
|
||||
if (image.valid(i, j)) {
|
||||
t++;
|
||||
QColor c = image.pixel(i, j);
|
||||
r += c.red();
|
||||
b += c.blue();
|
||||
g += c.green();
|
||||
}
|
||||
}
|
||||
}
|
||||
return QColor(int(bright * r / t) > 255 ? 255 : int(bright * r / t), int(bright * g / t) > 255 ? 255 : int(bright * g / t), int(bright * b / t) > 255 ? 255 : int(bright * b / t));
|
||||
}
|
||||
|
@ -92,4 +92,8 @@ SINGLETON(FluTools)
|
||||
Q_INVOKABLE bool isWindows10OrGreater();
|
||||
|
||||
Q_INVOKABLE QRect desktopAvailableGeometry(QQuickWindow *window);
|
||||
|
||||
Q_INVOKABLE QString getWallpaperFilePath();
|
||||
|
||||
Q_INVOKABLE QColor imageMainColor(const QImage& image, double bright = 1);
|
||||
};
|
@ -8,7 +8,7 @@ Item {
|
||||
property real tintOpacity: 0.65
|
||||
property real luminosity: 0.01
|
||||
property real noiseOpacity: 0.02
|
||||
property alias target: effect_source.sourceItem
|
||||
property var target
|
||||
property int blurRadius: 32
|
||||
property rect targetRect: Qt.rect(control.x, control.y, control.width,control.height)
|
||||
ShaderEffectSource {
|
||||
@ -16,6 +16,7 @@ Item {
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
sourceRect: control.targetRect
|
||||
sourceItem: control.target
|
||||
}
|
||||
FastBlur {
|
||||
id: fast_blur
|
||||
@ -33,7 +34,7 @@ Item {
|
||||
}
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: "../Image/noise.png"
|
||||
source: "qrc:/qt/qml/FluentUI/Image/noise.png"
|
||||
fillMode: Image.Tile
|
||||
opacity: control.noiseOpacity
|
||||
}
|
||||
|
@ -76,10 +76,11 @@ FluButton {
|
||||
}
|
||||
contentItem: Item{
|
||||
clip: true
|
||||
FluFrame{
|
||||
Rectangle{
|
||||
id:container
|
||||
width: 300
|
||||
height: 360
|
||||
color: FluTheme.dark ? Qt.rgba(51/255,48/255,48/255,1) : Qt.rgba(248/255,250/255,253/255,1)
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
@ -27,7 +27,12 @@ Item {
|
||||
height: 45
|
||||
radius: 4
|
||||
border.color: FluTheme.dividerColor
|
||||
color: FluTheme.dark ? Window.active ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1)
|
||||
color: {
|
||||
if(Window.active){
|
||||
return FluTheme.frameActiveColor
|
||||
}
|
||||
return FluTheme.frameColor
|
||||
}
|
||||
MouseArea{
|
||||
id:control_mouse
|
||||
anchors.fill: parent
|
||||
@ -84,12 +89,18 @@ Item {
|
||||
height: contentHeight+container.anchors.topMargin
|
||||
width: parent.width
|
||||
z:-999
|
||||
clip: true
|
||||
Rectangle{
|
||||
id:container
|
||||
anchors.fill: parent
|
||||
radius: 4
|
||||
clip: true
|
||||
color: FluTheme.dark ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1)
|
||||
color: {
|
||||
if(Window.active){
|
||||
return FluTheme.frameActiveColor
|
||||
}
|
||||
return FluTheme.frameColor
|
||||
}
|
||||
border.color: FluTheme.dividerColor
|
||||
anchors.topMargin: -contentHeight
|
||||
states: [
|
||||
|
@ -19,6 +19,11 @@ T.Frame {
|
||||
id:d
|
||||
radius: 4
|
||||
border.color: FluTheme.dividerColor
|
||||
color: FluTheme.dark ? Window.active ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(251/255,251/255,253/255,1)
|
||||
color: {
|
||||
if(Window.active){
|
||||
return FluTheme.frameActiveColor
|
||||
}
|
||||
return FluTheme.frameColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,13 @@ import FluentUI 1.0
|
||||
T.GroupBox {
|
||||
id: control
|
||||
property int borderWidth : 1
|
||||
property color borderColor : FluTheme.dark ? Window.active ? Qt.rgba(55/255,55/255,55/255,1):Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,229/255,234/255,1)
|
||||
property color color: FluTheme.dark ? Window.active ? Qt.rgba(38/255,44/255,54/255,1) : Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1)
|
||||
property color borderColor : FluTheme.dividerColor
|
||||
property color color: {
|
||||
if(Window.active){
|
||||
return FluTheme.frameActiveColor
|
||||
}
|
||||
return FluTheme.frameColor
|
||||
}
|
||||
property int radius: 4
|
||||
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
||||
contentWidth + leftPadding + rightPadding,
|
||||
|
@ -109,8 +109,53 @@ Window {
|
||||
}
|
||||
Component{
|
||||
id:com_background
|
||||
Rectangle{
|
||||
color: window.backgroundColor
|
||||
Item{
|
||||
Rectangle{
|
||||
anchors.fill: parent
|
||||
color: window.backgroundColor
|
||||
}
|
||||
Image{
|
||||
id:img_back
|
||||
visible: false
|
||||
cache: false
|
||||
Component.onCompleted: {
|
||||
var geometry = FluTools.desktopAvailableGeometry(window)
|
||||
width = geometry.width
|
||||
height = geometry.height
|
||||
sourceSize = Qt.size(width,height)
|
||||
source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath)
|
||||
}
|
||||
Connections{
|
||||
target: FluTheme
|
||||
function onDesktopImagePathChanged(){
|
||||
timer_update_image.restart()
|
||||
}
|
||||
function onBlurBehindWindowEnabledChanged(){
|
||||
if(FluTheme.blurBehindWindowEnabled){
|
||||
img_back.source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath)
|
||||
}else{
|
||||
img_back.source = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
Timer{
|
||||
id:timer_update_image
|
||||
interval: 500
|
||||
onTriggered: {
|
||||
img_back.source = ""
|
||||
img_back.source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
FluAcrylic{
|
||||
anchors.fill: parent
|
||||
target: img_back
|
||||
tintOpacity: FluTheme.dark ? 0.80 : 0.75
|
||||
blurRadius: 64
|
||||
visible: window.active && FluTheme.blurBehindWindowEnabled
|
||||
tintColor: FluTheme.dark ? Qt.rgba(0, 0, 0, 1) : Qt.rgba(1, 1, 1, 1)
|
||||
targetRect: Qt.rect(window.x,window.y,window.width,window.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
Component{
|
||||
|
@ -8,7 +8,7 @@ Item {
|
||||
property real tintOpacity: 0.65
|
||||
property real luminosity: 0.01
|
||||
property real noiseOpacity: 0.02
|
||||
property alias target: effect_source.sourceItem
|
||||
property var target
|
||||
property int blurRadius: 32
|
||||
property rect targetRect: Qt.rect(control.x, control.y, control.width,control.height)
|
||||
ShaderEffectSource {
|
||||
@ -16,6 +16,7 @@ Item {
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
sourceRect: control.targetRect
|
||||
sourceItem: control.target
|
||||
}
|
||||
FastBlur {
|
||||
id: fast_blur
|
||||
@ -33,7 +34,7 @@ Item {
|
||||
}
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: "../Image/noise.png"
|
||||
source: "qrc:/qt/qml/FluentUI/Image/noise.png"
|
||||
fillMode: Image.Tile
|
||||
opacity: control.noiseOpacity
|
||||
}
|
||||
|
@ -75,10 +75,11 @@ FluButton {
|
||||
}
|
||||
contentItem: Item{
|
||||
clip: true
|
||||
FluFrame{
|
||||
Rectangle{
|
||||
id:container
|
||||
width: 300
|
||||
height: 360
|
||||
color: FluTheme.dark ? Qt.rgba(51/255,48/255,48/255,1) : Qt.rgba(248/255,250/255,253/255,1)
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
@ -27,7 +27,12 @@ Item {
|
||||
height: 45
|
||||
radius: 4
|
||||
border.color: FluTheme.dividerColor
|
||||
color: FluTheme.dark ? Window.active ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1)
|
||||
color: {
|
||||
if(Window.active){
|
||||
return FluTheme.frameActiveColor
|
||||
}
|
||||
return FluTheme.frameColor
|
||||
}
|
||||
MouseArea{
|
||||
id:control_mouse
|
||||
anchors.fill: parent
|
||||
@ -84,12 +89,18 @@ Item {
|
||||
height: contentHeight+container.anchors.topMargin
|
||||
width: parent.width
|
||||
z:-999
|
||||
clip: true
|
||||
Rectangle{
|
||||
id:container
|
||||
anchors.fill: parent
|
||||
radius: 4
|
||||
clip: true
|
||||
color: FluTheme.dark ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1)
|
||||
color: {
|
||||
if(Window.active){
|
||||
return FluTheme.frameActiveColor
|
||||
}
|
||||
return FluTheme.frameColor
|
||||
}
|
||||
border.color: FluTheme.dividerColor
|
||||
anchors.topMargin: -contentHeight
|
||||
states: [
|
||||
|
@ -17,6 +17,11 @@ T.Frame {
|
||||
id:d
|
||||
radius: 4
|
||||
border.color: FluTheme.dividerColor
|
||||
color: FluTheme.dark ? Window.active ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(251/255,251/255,253/255,1)
|
||||
color: {
|
||||
if(Window.active){
|
||||
return FluTheme.frameActiveColor
|
||||
}
|
||||
return FluTheme.frameColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,13 @@ import FluentUI
|
||||
T.GroupBox {
|
||||
id: control
|
||||
property int borderWidth : 1
|
||||
property color borderColor : FluTheme.dark ? Window.active ? Qt.rgba(55/255,55/255,55/255,1):Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,229/255,234/255,1)
|
||||
property color color: FluTheme.dark ? Window.active ? Qt.rgba(38/255,44/255,54/255,1) : Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1)
|
||||
property color borderColor : FluTheme.dividerColor
|
||||
property color color: {
|
||||
if(Window.active){
|
||||
return FluTheme.frameActiveColor
|
||||
}
|
||||
return FluTheme.frameColor
|
||||
}
|
||||
property int radius: 4
|
||||
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
||||
contentWidth + leftPadding + rightPadding,
|
||||
|
@ -108,8 +108,53 @@ Window {
|
||||
}
|
||||
Component{
|
||||
id:com_background
|
||||
Rectangle{
|
||||
color: window.backgroundColor
|
||||
Item{
|
||||
Rectangle{
|
||||
anchors.fill: parent
|
||||
color: window.backgroundColor
|
||||
}
|
||||
Image{
|
||||
id:img_back
|
||||
visible: false
|
||||
cache: false
|
||||
Component.onCompleted: {
|
||||
var geometry = FluTools.desktopAvailableGeometry(window)
|
||||
width = geometry.width
|
||||
height = geometry.height
|
||||
sourceSize = Qt.size(width,height)
|
||||
source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath)
|
||||
}
|
||||
Connections{
|
||||
target: FluTheme
|
||||
function onDesktopImagePathChanged(){
|
||||
timer_update_image.restart()
|
||||
}
|
||||
function onBlurBehindWindowEnabledChanged(){
|
||||
if(FluTheme.blurBehindWindowEnabled){
|
||||
img_back.source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath)
|
||||
}else{
|
||||
img_back.source = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
Timer{
|
||||
id:timer_update_image
|
||||
interval: 500
|
||||
onTriggered: {
|
||||
img_back.source = ""
|
||||
img_back.source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
FluAcrylic{
|
||||
anchors.fill: parent
|
||||
target: img_back
|
||||
tintOpacity: FluTheme.dark ? 0.80 : 0.75
|
||||
blurRadius: 64
|
||||
visible: window.active && FluTheme.blurBehindWindowEnabled
|
||||
tintColor: FluTheme.dark ? Qt.rgba(0, 0, 0, 1) : Qt.rgba(1, 1, 1, 1)
|
||||
targetRect: Qt.rect(window.x,window.y,window.width,window.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
Component{
|
||||
|
Loading…
Reference in New Issue
Block a user