This commit is contained in:
zhuzichu 2023-09-12 18:45:15 +08:00
parent 05040ec4a9
commit fd30819393
20 changed files with 333 additions and 82 deletions

View File

@ -126,7 +126,6 @@
<file>qml/component/CustomWindow.qml</file> <file>qml/component/CustomWindow.qml</file>
<file>qml/global/ItemsFooter.qml</file> <file>qml/global/ItemsFooter.qml</file>
<file>qml/global/ItemsOriginal.qml</file> <file>qml/global/ItemsOriginal.qml</file>
<file>qml/global/MainEvent.qml</file>
<file>qml/global/qmldir</file> <file>qml/global/qmldir</file>
<file>qml/page/T_Acrylic.qml</file> <file>qml/page/T_Acrylic.qml</file>
<file>qml/page/T_Awesome.qml</file> <file>qml/page/T_Awesome.qml</file>
@ -186,5 +185,6 @@
<file>res/image/image_1.jpg</file> <file>res/image/image_1.jpg</file>
<file>qml/window/PageWindow.qml</file> <file>qml/window/PageWindow.qml</file>
<file>qml/page/T_StaggeredView.qml</file> <file>qml/page/T_StaggeredView.qml</file>
<file>qml/viewmodel/SettingsViewModel.qml</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -1,9 +0,0 @@
pragma Singleton
import QtQuick
import QtQuick.Controls
import FluentUI
QtObject {
property int displayMode : FluNavigationViewType.Auto
}

View File

@ -1,3 +1,2 @@
singleton ItemsOriginal 1.0 ItemsOriginal.qml singleton ItemsOriginal 1.0 ItemsOriginal.qml
singleton ItemsFooter 1.0 ItemsFooter.qml singleton ItemsFooter 1.0 ItemsFooter.qml
singleton MainEvent 1.0 MainEvent.qml

View File

@ -5,11 +5,15 @@ import QtQuick.Controls
import FluentUI import FluentUI
import "qrc:///example/qml/global" import "qrc:///example/qml/global"
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
import "qrc:///example/qml/viewmodel"
FluScrollablePage{ FluScrollablePage{
title:"Settings" title:"Settings"
SettingsViewModel{
id:viewmodel_settings
}
FluEvent{ FluEvent{
id:event_checkupdate_finish id:event_checkupdate_finish
@ -103,10 +107,10 @@ FluScrollablePage{
Repeater{ Repeater{
model: [{title:"Open",mode:FluNavigationViewType.Open},{title:"Compact",mode:FluNavigationViewType.Compact},{title:"Minimal",mode:FluNavigationViewType.Minimal},{title:"Auto",mode:FluNavigationViewType.Auto}] model: [{title:"Open",mode:FluNavigationViewType.Open},{title:"Compact",mode:FluNavigationViewType.Compact},{title:"Minimal",mode:FluNavigationViewType.Minimal},{title:"Auto",mode:FluNavigationViewType.Auto}]
delegate: FluRadioButton{ delegate: FluRadioButton{
checked : MainEvent.displayMode===modelData.mode checked : viewmodel_settings.displayMode===modelData.mode
text:modelData.title text:modelData.title
clickListener:function(){ clickListener:function(){
MainEvent.displayMode = modelData.mode viewmodel_settings.displayMode = modelData.mode
} }
} }
} }

View File

@ -0,0 +1,14 @@
import QtQuick
import FluentUI
import "qrc:///example/qml/component"
FluViewModel{
objectName: "SettingsViewModel"
property int displayMode
onInitData: {
displayMode = FluNavigationViewType.Auto
}
}

View File

@ -7,6 +7,7 @@ import FluentUI
import example import example
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
import "qrc:///example/qml/global" import "qrc:///example/qml/global"
import "qrc:///example/qml/viewmodel"
CustomWindow { CustomWindow {
@ -20,6 +21,10 @@ CustomWindow {
appBarVisible: false appBarVisible: false
launchMode: FluWindowType.SingleTask launchMode: FluWindowType.SingleTask
SettingsViewModel{
id:viewmodel_settings
}
closeFunc:function(event){ closeFunc:function(event){
dialog_close.open() dialog_close.open()
event.accepted = false event.accepted = false
@ -183,7 +188,7 @@ CustomWindow {
items: ItemsOriginal items: ItemsOriginal
footerItems:ItemsFooter footerItems:ItemsFooter
topPadding:FluTools.isMacos() ? 20 : 0 topPadding:FluTools.isMacos() ? 20 : 0
displayMode:MainEvent.displayMode displayMode:viewmodel_settings.displayMode
logo: "qrc:/example/res/image/favicon.ico" logo: "qrc:/example/res/image/favicon.ico"
title:"FluentUI" title:"FluentUI"
onLogoClicked:{ onLogoClicked:{

View File

@ -1,9 +0,0 @@
pragma Singleton
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
QtObject {
property int displayMode : FluNavigationViewType.Auto
}

View File

@ -1,3 +1,2 @@
singleton ItemsOriginal 1.0 ItemsOriginal.qml singleton ItemsOriginal 1.0 ItemsOriginal.qml
singleton ItemsFooter 1.0 ItemsFooter.qml singleton ItemsFooter 1.0 ItemsFooter.qml
singleton MainEvent 1.0 MainEvent.qml

View File

@ -16,9 +16,12 @@ FluScrollablePage{
height: 400 height: 400
paddings: 10 paddings: 10
FluPivot{ FluPivot{
anchors.fill: parent anchors.fill: parent
currentIndex: 2 currentIndex: 2
FluPivotItem{ FluPivotItem{
title:"All" title:"All"
contentItem:FluText{ contentItem:FluText{

View File

@ -5,12 +5,18 @@ import QtQuick.Controls 2.15
import FluentUI 1.0 import FluentUI 1.0
import "qrc:///example/qml/global" import "qrc:///example/qml/global"
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
import "qrc:///example/qml/viewmodel"
import "../component" import "../component"
import "../viewmodel"
import "../global"
FluScrollablePage{ FluScrollablePage{
title:"Settings" title:"Settings"
SettingsViewModel{
id:viewmodel_settings
}
FluEvent{ FluEvent{
id:event_checkupdate_finish id:event_checkupdate_finish
@ -104,10 +110,10 @@ FluScrollablePage{
Repeater{ Repeater{
model: [{title:"Open",mode:FluNavigationViewType.Open},{title:"Compact",mode:FluNavigationViewType.Compact},{title:"Minimal",mode:FluNavigationViewType.Minimal},{title:"Auto",mode:FluNavigationViewType.Auto}] model: [{title:"Open",mode:FluNavigationViewType.Open},{title:"Compact",mode:FluNavigationViewType.Compact},{title:"Minimal",mode:FluNavigationViewType.Minimal},{title:"Auto",mode:FluNavigationViewType.Auto}]
delegate: FluRadioButton{ delegate: FluRadioButton{
checked : MainEvent.displayMode===modelData.mode checked : viewmodel_settings.displayMode===modelData.mode
text:modelData.title text:modelData.title
clickListener:function(){ clickListener:function(){
MainEvent.displayMode = modelData.mode viewmodel_settings.displayMode = modelData.mode
} }
} }
} }
@ -150,4 +156,3 @@ FluScrollablePage{
} }
} }

View File

@ -0,0 +1,14 @@
import QtQuick 2.15
import FluentUI 1.0
import "qrc:///example/qml/component"
FluViewModel{
objectName: "SettingsViewModel"
property int displayMode
onInitData: {
displayMode = FluNavigationViewType.Auto
}
}

View File

@ -6,8 +6,11 @@ import Qt.labs.platform 1.1
import FluentUI 1.0 import FluentUI 1.0
import example 1.0 import example 1.0
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
import "../component"
import "qrc:///example/qml/global" import "qrc:///example/qml/global"
import "qrc:///example/qml/viewmodel"
import "../component"
import "../viewmodel"
import "../global"
CustomWindow { CustomWindow {
@ -21,6 +24,10 @@ CustomWindow {
appBarVisible: false appBarVisible: false
launchMode: FluWindowType.SingleTask launchMode: FluWindowType.SingleTask
SettingsViewModel{
id:viewmodel_settings
}
closeFunc:function(event){ closeFunc:function(event){
dialog_close.open() dialog_close.open()
event.accepted = false event.accepted = false
@ -184,7 +191,7 @@ CustomWindow {
items: ItemsOriginal items: ItemsOriginal
footerItems:ItemsFooter footerItems:ItemsFooter
topPadding:FluTools.isMacos() ? 20 : 0 topPadding:FluTools.isMacos() ? 20 : 0
displayMode:MainEvent.displayMode displayMode:viewmodel_settings.displayMode
logo: "qrc:/example/res/image/favicon.ico" logo: "qrc:/example/res/image/favicon.ico"
title:"FluentUI" title:"FluentUI"
onLogoClicked:{ onLogoClicked:{

View File

@ -4,6 +4,16 @@
#include <QObject> #include <QObject>
#include <QtQml/qqml.h> #include <QtQml/qqml.h>
namespace FluViewModelType {
Q_NAMESPACE
enum Scope {
Window = 0x0000,
Application = 0x0001
};
Q_ENUM_NS(Scope)
QML_NAMED_ELEMENT(FluViewModelType)
}
namespace FluHttpType { namespace FluHttpType {
Q_NAMESPACE Q_NAMESPACE
enum CacheMode { enum CacheMode {

139
src/FluViewModel.cpp Normal file
View File

@ -0,0 +1,139 @@
#include "FluViewModel.h"
#include <QQuickItem>
#include "Def.h"
ViewModelManager* ViewModelManager::m_instance = nullptr;
ViewModelManager *ViewModelManager::getInstance()
{
if(ViewModelManager::m_instance == nullptr){
ViewModelManager::m_instance = new ViewModelManager;
}
return ViewModelManager::m_instance;
}
Model::Model(QObject *parent)
: QObject{parent}
{
}
Model::~Model()
{
qDebug()<<"Model delete~";
}
ViewModelManager::ViewModelManager(QObject *parent)
: QObject{parent}
{
}
void ViewModelManager::insertViewModel(FluViewModel* value){
m_viewmodel.append(value);
}
void ViewModelManager::deleteViewModel(FluViewModel* value){
m_viewmodel.removeOne(value);
}
QObject* ViewModelManager::getModel(const QString& key){
return m_data.value(key);
}
void ViewModelManager::insert(const QString& key,QObject* value){
m_data.insert(key,value);
}
bool ViewModelManager::exist(const QString& key){
return m_data.contains(key);
}
void ViewModelManager::refreshViewModel(FluViewModel* viewModel,QString key,QVariant value){
foreach (auto item, m_viewmodel) {
if(item->getKey() == viewModel->getKey()){
item->setProperty(key.toStdString().c_str(),value);
}
}
}
PropertyObserver::PropertyObserver(QString name,QObject* model,QObject *parent)
: QObject{parent}
{
_name = name;
_model = model;
_property = QQmlProperty(parent,_name);
_property.connectNotifySignal(this,SLOT(_propertyChange()));
}
PropertyObserver::~PropertyObserver(){
qDebug()<<"PropertyObserver delete~";
}
void PropertyObserver::_propertyChange(){
auto value = _property.read();
_model->setProperty(_name.toStdString().c_str(),value);
ViewModelManager::getInstance()->refreshViewModel((FluViewModel*)parent(),_name,value);
}
FluViewModel::FluViewModel(QObject *parent)
: QObject{parent}
{
ViewModelManager::getInstance()->insertViewModel(this);
scope(FluViewModelType::Scope::Window);
}
FluViewModel::~FluViewModel(){
ViewModelManager::getInstance()->deleteViewModel(this);
}
void FluViewModel::classBegin()
{
}
void FluViewModel::componentComplete()
{
auto o = parent();
while (nullptr != o) {
_window = o;
o = o->parent();
}
const QMetaObject* obj = metaObject();
if(_scope == FluViewModelType::Scope::Window){
_key = property("objectName_").toString()+QString::number(reinterpret_cast<qulonglong>(_window), 16);
}else{
_key = property("objectName").toString();
}
QObject * model;
if(!ViewModelManager::getInstance()->exist(_key)){
if(_scope == FluViewModelType::Scope::Window){
model = new Model(_window);
}else{
model = new Model();
}
Q_EMIT initData();
for (int i = 0; i < obj->propertyCount(); ++i) {
const QMetaProperty property = obj->property(i);
QString propertyName = property.name();
auto value = property.read(this);
model->setProperty(propertyName.toStdString().c_str(),value);
new PropertyObserver(propertyName,model,this);
}
ViewModelManager::getInstance()->insert(_key,model);
}else{
model = ViewModelManager::getInstance()->getModel(_key);
for (int i = 0; i < obj->propertyCount(); ++i) {
const QMetaProperty property = obj->property(i);
QString propertyName = property.name();
new PropertyObserver(propertyName,model,this);
}
}
foreach (auto key, model->dynamicPropertyNames()) {
setProperty(key,model->property(key));
}
}
QString FluViewModel::getKey(){
return _key;
}

68
src/FluViewModel.h Normal file
View File

@ -0,0 +1,68 @@
#ifndef FLUVIEWMODEL_H
#define FLUVIEWMODEL_H
#include <QQuickItem>
#include <QtQml/qqml.h>
#include <QQuickWindow>
#include <QQmlProperty>
#include "stdafx.h"
class Model : public QObject{
Q_OBJECT
public:
explicit Model(QObject *parent = nullptr);
~Model();
};
class FluViewModel : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
Q_PROPERTY_AUTO(int,scope);
Q_PROPERTY_AUTO(QObject*,target);
QML_NAMED_ELEMENT(FluViewModel)
public:
explicit FluViewModel(QObject *parent = nullptr);
~FluViewModel();
void classBegin() override;
void componentComplete() override;
Q_SIGNAL void initData();
QString getKey();
private:
QObject* _window = nullptr;
QString _key;
};
class PropertyObserver: public QObject{
Q_OBJECT
public:
explicit PropertyObserver(QString name,QObject* model,QObject *parent = nullptr);
~PropertyObserver();
private:
Q_SLOT void _propertyChange();
private:
QString _name;
QQmlProperty _property;
QObject* _model;
};
class ViewModelManager:public QObject{
Q_OBJECT
private:
explicit ViewModelManager(QObject *parent = nullptr);
public:
static ViewModelManager *getInstance();
bool exist(const QString& key);
void insert(const QString& key,QObject* value);
QObject* getModel(const QString& key);
void insertViewModel(FluViewModel* value);
void deleteViewModel(FluViewModel* value);
void refreshViewModel(FluViewModel* viewModel,QString key,QVariant value);
private:
static ViewModelManager* m_instance;
QMap<QString,QObject*> m_data;
QList<FluViewModel*> m_viewmodel;
};
#endif // FLUVIEWMODEL_H

View File

@ -13,6 +13,7 @@
#include "FluWatermark.h" #include "FluWatermark.h"
#include "FluCaptcha.h" #include "FluCaptcha.h"
#include "FluEventBus.h" #include "FluEventBus.h"
#include "FluViewModel.h"
#include "Screenshot.h" #include "Screenshot.h"
#include "QRCode.h" #include "QRCode.h"
@ -51,6 +52,7 @@ void FluentUI::registerTypes(const char *uri){
qmlRegisterType<HttpCallable>(uri,major,minor,"HttpCallable"); qmlRegisterType<HttpCallable>(uri,major,minor,"HttpCallable");
qmlRegisterType<HttpRequest>(uri,major,minor,"HttpRequest"); qmlRegisterType<HttpRequest>(uri,major,minor,"HttpRequest");
qmlRegisterType<FluEvent>(uri,major,minor,"FluEvent"); qmlRegisterType<FluEvent>(uri,major,minor,"FluEvent");
qmlRegisterType<FluViewModel>(uri,major,minor,"FluViewModel");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/ColorPicker/ColorPicker.qml"),uri,major,minor,"ColorPicker"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/ColorPicker/ColorPicker.qml"),uri,major,minor,"ColorPicker");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/ColorPicker/Content/Checkerboard.qml"),uri,major,minor,"Checkerboard"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/ColorPicker/Content/Checkerboard.qml"),uri,major,minor,"Checkerboard");

View File

@ -124,7 +124,6 @@ Item {
Component{ Component{
id:com_panel_item_header id:com_panel_item_header
Item{ Item{
clip: true
height: { height: {
if(model.parent){ if(model.parent){
return model.parent.isExpand ? 30 : 0 return model.parent.isExpand ? 30 : 0
@ -154,7 +153,6 @@ Item {
Item{ Item{
height: 38 height: 38
width: layout_list.width width: layout_list.width
clip: true
FluControl{ FluControl{
id:item_control id:item_control
anchors{ anchors{
@ -378,7 +376,6 @@ Item {
duration: 83 duration: 83
} }
} }
clip: true
height: { height: {
if(model.parent){ if(model.parent){
return model.parent.isExpand ? 38 : 0 return model.parent.isExpand ? 38 : 0

View File

@ -2,30 +2,32 @@ import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import FluentUI 1.0 import FluentUI 1.0
Item { Page {
default property alias content: d.children default property alias content: d.children
property alias currentIndex: nav_list.currentIndex property alias currentIndex: nav_list.currentIndex
property color normalColor: FluTheme.dark ? FluColors.Grey120 : FluColors.Grey120 property color textNormalColor: FluTheme.dark ? FluColors.Grey120 : FluColors.Grey120
property color hoverColor: FluTheme.dark ? FluColors.Grey10 : FluColors.Black property color textHoverColor: FluTheme.dark ? FluColors.Grey10 : FluColors.Black
property int textSize: 28
property bool textBold: true
property int textSpacing: 10
property int headerSpacing: 20
property int headerHeight: 40
id:control id:control
width: 400 width: 400
height: 300 height: 300
implicitHeight: height implicitHeight: height
implicitWidth: width implicitWidth: width
MouseArea{
anchors.fill: parent
preventStealing: true
}
FluObject{ FluObject{
id:d id:d
property int tabY: control.headerHeight/2+control.textSize/2 + 3
} }
ListView{ background:Item{}
header:ListView{
id:nav_list id:nav_list
height: 40 implicitHeight: control.headerHeight
width: control.width implicitWidth: control.width
model:d.children model:d.children
clip: true spacing: control.headerSpacing
spacing: 20
interactive: false interactive: false
orientation: ListView.Horizontal orientation: ListView.Horizontal
highlightMoveDuration: FluTheme.enableAnimation ? 167 : 0 highlightMoveDuration: FluTheme.enableAnimation ? 167 : 0
@ -36,7 +38,7 @@ Item {
radius: 1.5 radius: 1.5
color: FluTheme.primaryColor.dark color: FluTheme.primaryColor.dark
width: nav_list.currentItem ? nav_list.currentItem.width : 0 width: nav_list.currentItem ? nav_list.currentItem.width : 0
y:37 y:d.tabY
Behavior on width { Behavior on width {
enabled: FluTheme.enableAnimation enabled: FluTheme.enableAnimation
NumberAnimation{ NumberAnimation{
@ -50,18 +52,25 @@ Item {
id:item_button id:item_button
width: item_title.width width: item_title.width
height: nav_list.height height: nav_list.height
focusPolicy:Qt.TabFocus
background:Item{ background:Item{
FluFocusRectangle{
anchors.margins: -4
visible: item_button.activeFocus
radius:4
}
} }
contentItem: Item{ contentItem: Item{
FluText { FluText {
id:item_title id:item_title
font: FluTextStyle.Title
text: modelData.title text: modelData.title
anchors.centerIn: parent anchors.centerIn: parent
font.pixelSize: control.textSize
font.bold: control.textBold
color: { color: {
if(item_button.hovered) if(item_button.hovered)
return hoverColor return textHoverColor
return normalColor return textNormalColor
} }
} }
} }
@ -72,13 +81,7 @@ Item {
} }
Item{ Item{
id:container id:container
anchors{ anchors.fill: parent
top: nav_list.bottom
topMargin: 10
left: parent.left
right: parent.right
bottom: parent.bottom
}
Repeater{ Repeater{
model:d.children model:d.children
Loader{ Loader{

View File

@ -125,7 +125,6 @@ Item {
Component{ Component{
id:com_panel_item_header id:com_panel_item_header
Item{ Item{
clip: true
height: { height: {
if(model.parent){ if(model.parent){
return model.parent.isExpand ? 30 : 0 return model.parent.isExpand ? 30 : 0
@ -155,7 +154,6 @@ Item {
Item{ Item{
height: 38 height: 38
width: layout_list.width width: layout_list.width
clip: true
FluControl{ FluControl{
id:item_control id:item_control
anchors{ anchors{
@ -379,7 +377,6 @@ Item {
duration: 83 duration: 83
} }
} }
clip: true
height: { height: {
if(model.parent){ if(model.parent){
return model.parent.isExpand ? 38 : 0 return model.parent.isExpand ? 38 : 0

View File

@ -2,30 +2,32 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import FluentUI import FluentUI
Item { Page {
default property alias content: d.children default property alias content: d.children
property alias currentIndex: nav_list.currentIndex property alias currentIndex: nav_list.currentIndex
property color normalColor: FluTheme.dark ? FluColors.Grey120 : FluColors.Grey120 property color textNormalColor: FluTheme.dark ? FluColors.Grey120 : FluColors.Grey120
property color hoverColor: FluTheme.dark ? FluColors.Grey10 : FluColors.Black property color textHoverColor: FluTheme.dark ? FluColors.Grey10 : FluColors.Black
property int textSize: 28
property bool textBold: true
property int textSpacing: 10
property int headerSpacing: 20
property int headerHeight: 40
id:control id:control
width: 400 width: 400
height: 300 height: 300
implicitHeight: height implicitHeight: height
implicitWidth: width implicitWidth: width
MouseArea{
anchors.fill: parent
preventStealing: true
}
FluObject{ FluObject{
id:d id:d
property int tabY: control.headerHeight/2+control.textSize/2 + 3
} }
ListView{ background:Item{}
header:ListView{
id:nav_list id:nav_list
height: 40 implicitHeight: control.headerHeight
width: control.width implicitWidth: control.width
model:d.children model:d.children
clip: true spacing: control.headerSpacing
spacing: 20
interactive: false interactive: false
orientation: ListView.Horizontal orientation: ListView.Horizontal
highlightMoveDuration: FluTheme.enableAnimation ? 167 : 0 highlightMoveDuration: FluTheme.enableAnimation ? 167 : 0
@ -36,7 +38,7 @@ Item {
radius: 1.5 radius: 1.5
color: FluTheme.primaryColor.dark color: FluTheme.primaryColor.dark
width: nav_list.currentItem ? nav_list.currentItem.width : 0 width: nav_list.currentItem ? nav_list.currentItem.width : 0
y:37 y:d.tabY
Behavior on width { Behavior on width {
enabled: FluTheme.enableAnimation enabled: FluTheme.enableAnimation
NumberAnimation{ NumberAnimation{
@ -50,18 +52,25 @@ Item {
id:item_button id:item_button
width: item_title.width width: item_title.width
height: nav_list.height height: nav_list.height
focusPolicy:Qt.TabFocus
background:Item{ background:Item{
FluFocusRectangle{
anchors.margins: -4
visible: item_button.activeFocus
radius:4
}
} }
contentItem: Item{ contentItem: Item{
FluText { FluText {
id:item_title id:item_title
font: FluTextStyle.Title
text: modelData.title text: modelData.title
anchors.centerIn: parent anchors.centerIn: parent
font.pixelSize: control.textSize
font.bold: control.textBold
color: { color: {
if(item_button.hovered) if(item_button.hovered)
return hoverColor return textHoverColor
return normalColor return textNormalColor
} }
} }
} }
@ -72,13 +81,7 @@ Item {
} }
Item{ Item{
id:container id:container
anchors{ anchors.fill: parent
top: nav_list.bottom
topMargin: 10
left: parent.left
right: parent.right
bottom: parent.bottom
}
Repeater{ Repeater{
model:d.children model:d.children
Loader{ Loader{