diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d416420..3ef5a1d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ if(MSVC) set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /DEBUG /OPT:REF /OPT:ICF") endif() -list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/.cmake/) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/.cmake/) include(GetGitRevisionDescription) diff --git a/example/example_en_US.ts b/example/example_en_US.ts index f20047c2..47476754 100644 --- a/example/example_en_US.ts +++ b/example/example_en_US.ts @@ -475,46 +475,51 @@ - QRCode + CodeEditor - Tour + QRCode - Timeline + Tour - Captcha + Timeline + Captcha + + + + Network - + Remote Loader - + Hot Loader - + 3D - + Test Crash @@ -2376,13 +2381,23 @@ Some contents... - - Total %1 data, %2 data currently displayed + + Title - - A total of %1 data items are selected + + Avatar + + + + + Address + + + + + Name diff --git a/example/example_zh_CN.ts b/example/example_zh_CN.ts index 048a6c55..8ae59fac 100644 --- a/example/example_zh_CN.ts +++ b/example/example_zh_CN.ts @@ -475,46 +475,51 @@ + CodeEditor + + + + QRCode 二维码 - + Tour 游览 - + Timeline 时间轴 - + Captcha 验证码 - + Network 网络 - + Remote Loader 远程加载 - + Hot Loader 热加载 - + 3D 3D - + Test Crash 测试崩溃 @@ -2459,14 +2464,32 @@ Some contents... - Total %1 data, %2 data currently displayed - 共计%1条数据,当前显示的%2条数据 + 共计%1条数据,当前显示的%2条数据 - A total of %1 data items are selected - 共计选中%1条数据 + 共计选中%1条数据 + + + + Title + 标题 + + + + Address + 地址 + + + + Avatar + 头像 + + + + Name + 名称 diff --git a/example/qml/global/ItemsOriginal.qml b/example/qml/global/ItemsOriginal.qml index 47615f0a..8e96b2e0 100644 --- a/example/qml/global/ItemsOriginal.qml +++ b/example/qml/global/ItemsOriginal.qml @@ -426,6 +426,12 @@ FluObject{ FluPaneItemExpander{ title: qsTr("Other") icon: FluentIcons.Shop + FluPaneItem{ + title: qsTr("CodeEditor") + menuDelegate: paneItemMenu + url: "qrc:/example/qml/page/T_CodeEditor.qml" + onTap: { navigationView.push(url) } + } FluPaneItem{ title: qsTr("QRCode") menuDelegate: paneItemMenu diff --git a/example/qml/page/T_TreeView.qml b/example/qml/page/T_TreeView.qml index df7d47f2..c1ead5f5 100644 --- a/example/qml/page/T_TreeView.qml +++ b/example/qml/page/T_TreeView.qml @@ -9,14 +9,34 @@ FluContentPage { title: qsTr("TreeView") + + function treeData(){ - const dig = (path = '0', level = 4) => { + const names = ["孙悟空", "猪八戒", "沙和尚", "唐僧","白骨夫人","金角大王","熊山君","黄风怪","银角大王"] + function getRandomName(){ + var randomIndex = Math.floor(Math.random() * names.length) + return names[randomIndex] + } + const addresses = ["傲来国界花果山水帘洞","傲来国界坎源山脏水洞","大唐国界黑风山黑风洞","大唐国界黄风岭黄风洞","大唐国界骷髅山白骨洞","宝象国界碗子山波月洞","宝象国界平顶山莲花洞","宝象国界压龙山压龙洞","乌鸡国界号山枯松涧火云洞","乌鸡国界衡阳峪黑水河河神府"] + function getRandomAddresses(){ + var randomIndex = Math.floor(Math.random() * addresses.length) + return addresses[randomIndex] + } + const avatars = ["qrc:/example/res/svg/avatar_1.svg", "qrc:/example/res/svg/avatar_2.svg", "qrc:/example/res/svg/avatar_3.svg", "qrc:/example/res/svg/avatar_4.svg","qrc:/example/res/svg/avatar_5.svg","qrc:/example/res/svg/avatar_6.svg","qrc:/example/res/svg/avatar_7.svg","qrc:/example/res/svg/avatar_8.svg","qrc:/example/res/svg/avatar_9.svg","qrc:/example/res/svg/avatar_10.svg","qrc:/example/res/svg/avatar_11.svg","qrc:/example/res/svg/avatar_12.svg"] + function getRandomAvatar(){ + var randomIndex = Math.floor(Math.random() * avatars.length); + return avatars[randomIndex]; + } + const dig = (path = '0', level = 5) => { const list = []; - for (let i = 0; i < 6; i += 1) { + for (let i = 0; i < 5; i += 1) { const key = `${path}-${i}`; const treeNode = { title: key, - key, + _key: key, + name: getRandomName(), + avatar:tree_view.customItem(com_avatar,{avatar:getRandomAvatar()}), + address: getRandomAddresses() }; if (level > 0) { treeNode.children = dig(key, level - 1); @@ -28,99 +48,202 @@ FluContentPage { return dig(); } - Column{ - id: layout_column - spacing: 12 - width: 300 - anchors{ - top:parent.top - left: parent.left - leftMargin: 10 - bottom:parent.bottom - bottomMargin: 20 - } + // Row{ + // id: layout_column + // spacing: 12 + // width: 340 + // anchors{ + // top:parent.top + // left: parent.left + // leftMargin: 10 + // bottom:parent.bottom + // bottomMargin: 20 + // } + // RowLayout{ + // spacing: 10 + // FluText{ + // text: "cellHeight:" + // Layout.alignment: Qt.AlignVCenter + // } + // FluSlider{ + // id: slider_cell_height + // value: 30 + // from: 30 + // to:100 + // } + // } + // RowLayout{ + // spacing: 10 + // FluText{ + // text: "depthPadding:" + // Layout.alignment: Qt.AlignVCenter + // } + // FluSlider{ + // id: slider_depth_padding + // value: 15 + // from: 15 + // to:100 + // } + // } + // FluToggleSwitch{ + // id: switch_showline + // text:"showLine" + // checked: false + // } + // FluToggleSwitch{ + // id: switch_checkable + // text:"checkable" + // checked: false + // } + // FluButton{ + // text: "all expand" + // onClicked: { + // tree_view.allExpand() + // } + // } + // FluButton{ + // text: "all collapse" + // onClicked: { + // tree_view.allCollapse() + // } + // } + // } - FluText{ - text: qsTr("Total %1 data, %2 data currently displayed").arg(tree_view.count()).arg(tree_view.visibleCount()) - } - - FluText{ - text: qsTr("A total of %1 data items are selected").arg(tree_view.selectionModel().length) - } - - RowLayout{ - spacing: 10 - FluText{ - text: "cellHeight:" - Layout.alignment: Qt.AlignVCenter - } - FluSlider{ - id: slider_cell_height - value: 30 - from: 30 - to:100 - } - } - RowLayout{ - spacing: 10 - FluText{ - text: "depthPadding:" - Layout.alignment: Qt.AlignVCenter - } - FluSlider{ - id: slider_depth_padding - value: 30 - from: 30 - to:100 - } - } - FluToggleSwitch{ - id: switch_showline - text:"showLine" - checked: false - } - FluToggleSwitch{ - id: switch_draggable - text:"draggable" - checked: false - } - FluToggleSwitch{ - id: switch_checkable - text:"checkable" - checked: false - } - FluButton{ - text: "all expand" - onClicked: { - tree_view.allExpand() - } - } - FluButton{ - text: "all collapse" - onClicked: { - tree_view.allCollapse() + Component{ + id:com_avatar + Item{ + FluClip{ + anchors.centerIn: parent + width: height + height: parent.height/3*2 + radius: [height/2,height/2,height/2,height/2] + Image{ + anchors.fill: parent + source: { + if(options && options.avatar){ + return options.avatar + } + return "" + } + sourceSize: Qt.size(80,80) + } } } } FluFrame{ + id:layout_controls anchors{ - left: layout_column.right + left: parent.left + right: parent.right top: parent.top + topMargin: 10 + } + height: 80 + clip: true + Row{ + spacing: 12 + anchors{ + left: parent.left + leftMargin: 10 + verticalCenter: parent.verticalCenter + } + Column{ + anchors.verticalCenter: parent.verticalCenter + RowLayout{ + spacing: 10 + FluText{ + text: "cellHeight:" + Layout.alignment: Qt.AlignVCenter + } + FluSlider{ + id: slider_cell_height + value: 38 + from: 38 + to:100 + } + } + RowLayout{ + spacing: 10 + FluText{ + text: "depthPadding:" + Layout.alignment: Qt.AlignVCenter + } + FluSlider{ + id: slider_depth_padding + value: 15 + from: 15 + to:100 + } + } + } + Column{ + spacing: 8 + anchors.verticalCenter: parent.verticalCenter + FluToggleSwitch{ + id: switch_showline + text:"showLine" + checked: false + } + FluToggleSwitch{ + id: switch_checkable + text:"checkable" + checked: false + } + } + Column{ + spacing: 8 + anchors.verticalCenter: parent.verticalCenter + FluButton{ + text: "all expand" + onClicked: { + tree_view.allExpand() + } + } + FluButton{ + text: "all collapse" + onClicked: { + tree_view.allCollapse() + } + } + } + + } + } + + FluFrame{ + anchors{ + left: parent.left + top: layout_controls.bottom + topMargin: 10 bottom: parent.bottom right: parent.right - rightMargin: 5 - topMargin: 5 - bottomMargin: 5 } - FluShadow{} FluTreeView{ id:tree_view anchors.fill: parent cellHeight: slider_cell_height.value - draggable:switch_draggable.checked showLine: switch_showline.checked checkable:switch_checkable.checked depthPadding: slider_depth_padding.value + columnSource:[ + { + title: qsTr("Title"), + dataIndex: 'title', + width: 300 + },{ + title: qsTr("Name"), + dataIndex: 'name', + width: 100 + },{ + title: qsTr("Avatar"), + dataIndex: 'avatar', + width: 100 + },{ + title: qsTr("Address"), + dataIndex: 'address', + width: 200 + }, + ] Component.onCompleted: { var data = treeData() dataSource = data diff --git a/src/FluTreeModel.cpp b/src/FluTreeModel.cpp index f53fede8..d2c3b5b9 100644 --- a/src/FluTreeModel.cpp +++ b/src/FluTreeModel.cpp @@ -24,13 +24,15 @@ int FluTreeModel::rowCount(const QModelIndex &parent) const { }; int FluTreeModel::columnCount(const QModelIndex &parent) const { - return 1;; + return this->_columnSource.size(); }; QVariant FluTreeModel::data(const QModelIndex &index, int role) const { switch (role) { - case Qt::DisplayRole: - return QVariant::fromValue(_rows.at(index.row())); + case TreeModelRoles::RowModel: + return QVariant::fromValue(_rows.at(index.row())); + case TreeModelRoles::ColumnModel: + return QVariant::fromValue(_columnSource.at(index.column())); default: break; } @@ -38,7 +40,10 @@ QVariant FluTreeModel::data(const QModelIndex &index, int role) const { }; QHash FluTreeModel::roleNames() const { - return { {Qt::DisplayRole, "dataModel"} }; + return { + {TreeModelRoles::RowModel, "rowModel"}, + {TreeModelRoles::ColumnModel, "columnModel"} + }; }; void FluTreeModel::setData(QList data){ @@ -76,6 +81,11 @@ QObject* FluTreeModel::getRow(int row){ return _rows.at(row); } +void FluTreeModel::setRow(int row,QVariantMap data){ + _rows.at(row)->_data = data; + Q_EMIT dataChanged(index(row,0),index(row,columnCount()-1)); +} + void FluTreeModel::checkRow(int row,bool checked){ auto itemData = _rows.at(row); if(itemData->hasChildren()){ @@ -125,10 +135,9 @@ void FluTreeModel::setDataSource(QList> data){ auto item = data.at(data.count()-1); data.pop_back(); FluTreeNode* node = new FluTreeNode(this); - node->_title = item.value("title").toString(); - node->_key = item.value("key").toString(); node->_depth = item.value("__depth").toInt(); node->_parent = item.value("__parent").value(); + node->_data = item; node->_isExpanded = true; if(node->_parent){ node->_parent->_children.append(node); @@ -201,110 +210,6 @@ void FluTreeModel::expand(int row){ insertRows(row+1,insertData); } -void FluTreeModel::dragAndDrop(int dragIndex,int dropIndex,bool isDropTopArea){ - if(dropIndex>_rows.count() || dropIndex<0){ - return; - } - auto dragItem = _rows[dragIndex]; - auto dropItem = _rows[dropIndex]; - int targetIndex; - if(dropIndex > dragIndex){ - if(isDropTopArea){ - targetIndex = dropIndex; - }else{ - targetIndex = dropIndex+1; - } - }else{ - if(isDropTopArea){ - targetIndex = dropIndex; - }else{ - targetIndex = dropIndex+1; - } - } - if (!beginMoveRows(QModelIndex(), dragIndex, dragIndex, QModelIndex(), targetIndex)) { - return; - } - if(dropIndex > dragIndex){ - if(isDropTopArea){ - targetIndex = dropIndex-1; - }else{ - targetIndex = dropIndex; - } - }else{ - if(isDropTopArea){ - targetIndex = dropIndex; - }else{ - targetIndex = dropIndex+1; - } - } - _rows.move(dragIndex,targetIndex); - endMoveRows(); - - Q_EMIT layoutAboutToBeChanged(); - if(dragItem->_parent == dropItem->_parent){ - QList* children = &(dragItem->_parent->_children); - int srcIndex = children->indexOf(dragItem); - int destIndex = children->indexOf(dropItem); - if(dropIndex > dragIndex){ - if(isDropTopArea){ - targetIndex = destIndex-1; - }else{ - targetIndex = destIndex; - } - }else{ - if(isDropTopArea){ - targetIndex = destIndex; - }else{ - targetIndex = destIndex+1; - } - } - children->move(srcIndex,targetIndex); - }else{ - QList* srcChildren = &(dragItem->_parent->_children); - QList* destChildren = &(dropItem->_parent->_children); - int srcIndex = srcChildren->indexOf(dragItem); - int destIndex = destChildren->indexOf(dropItem); - dragItem->_depth = dropItem->_depth; - dragItem->_parent = dropItem->_parent; - if(dragItem->hasChildren()){ - QList stack = dragItem->_children; - foreach (auto node, stack) { - node->_depth = dragItem->_depth+1; - } - std::reverse(stack.begin(), stack.end()); - while (stack.count() > 0) { - auto item = stack.at(stack.count()-1); - stack.pop_back(); - QList children = item->_children; - if(!children.isEmpty()){ - std::reverse(children.begin(), children.end()); - foreach (auto c, children) { - c->_depth = item->_depth+1; - stack.append(c); - } - } - } - } - srcChildren->removeAt(srcIndex); - if(dropIndex > dragIndex){ - if(isDropTopArea){ - targetIndex = destIndex; - }else{ - targetIndex = destIndex + 1; - } - }else{ - if(isDropTopArea){ - targetIndex = destIndex; - }else{ - targetIndex = destIndex + 1; - } - } - destChildren->insert(targetIndex,dragItem); - } - changePersistentIndex(index(qMin(dragIndex,dropIndex),0),index(qMax(dragIndex,dropIndex),0)); - Q_EMIT dataChanged(index(0,0),index(rowCount()-1,0)); -} - bool FluTreeModel::hitHasChildrenExpanded(int row){ auto itemData = _rows.at(row); if(itemData->hasChildren() && itemData->_isExpanded){ diff --git a/src/FluTreeModel.h b/src/FluTreeModel.h index 9b588d3d..a64e1efb 100644 --- a/src/FluTreeModel.h +++ b/src/FluTreeModel.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "stdafx.h" @@ -12,17 +13,15 @@ */ class FluTreeNode : public QObject{ Q_OBJECT - Q_PROPERTY(QString key READ key CONSTANT) - Q_PROPERTY(QString title READ title CONSTANT) + Q_PROPERTY(QVariantMap data READ data CONSTANT) Q_PROPERTY(int depth READ depth CONSTANT) Q_PROPERTY(bool isExpanded READ isExpanded CONSTANT) Q_PROPERTY(bool checked READ checked CONSTANT) public: explicit FluTreeNode(QObject *parent = nullptr); - Q_INVOKABLE QString key(){return _key;}; - Q_INVOKABLE QString title(){return _title;}; Q_INVOKABLE int depth(){return _depth;}; Q_INVOKABLE bool isExpanded(){return _isExpanded;}; + Q_INVOKABLE QVariantMap data(){return _data;}; Q_INVOKABLE bool hasChildren(){ return !_children.isEmpty();}; Q_INVOKABLE bool hasNextNodeByIndex(int index){ FluTreeNode* p = this; @@ -70,11 +69,11 @@ public: return true; } public: - QString _key=""; QString _title=""; int _depth=0; bool _checked = false; bool _isExpanded=true; + QVariantMap _data; QList _children; FluTreeNode* _parent = nullptr; }; @@ -84,9 +83,14 @@ class FluTreeModel : public QAbstractItemModel Q_OBJECT Q_PROPERTY_AUTO(int,dataSourceSize) Q_PROPERTY_AUTO(QList,selectionModel) + Q_PROPERTY_AUTO(QList,columnSource) QML_NAMED_ELEMENT(FluTreeModel) QML_ADDED_IN_MINOR_VERSION(1) public: + enum TreeModelRoles { + RowModel = 0x0101, + ColumnModel = 0x0102 + }; explicit FluTreeModel(QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; @@ -98,11 +102,11 @@ public: Q_INVOKABLE void removeRows(int row,int count); Q_INVOKABLE void insertRows(int row,QList data); Q_INVOKABLE QObject* getRow(int row); + Q_INVOKABLE void setRow(int row,QVariantMap data); Q_INVOKABLE void setData(QList data); Q_INVOKABLE void setDataSource(QList> data); Q_INVOKABLE void collapse(int row); Q_INVOKABLE void expand(int row); - Q_INVOKABLE void dragAndDrop(int dragIndex,int dropIndex,bool isDropTopArea); Q_INVOKABLE FluTreeNode* getNode(int row); Q_INVOKABLE void refreshNode(int row); Q_INVOKABLE void checkRow(int row,bool checked); diff --git a/src/FluWatermark.cpp b/src/FluWatermark.cpp index 363de1f5..54f2405e 100644 --- a/src/FluWatermark.cpp +++ b/src/FluWatermark.cpp @@ -1,5 +1,7 @@ #include "FluWatermark.h" +#include "FluTextStyle.h" + FluWatermark::FluWatermark(QQuickItem* parent) : QQuickPaintedItem(parent){ gap(QPoint(100,100)); offset(QPoint(_gap.x()/2,_gap.y()/2)); @@ -17,6 +19,7 @@ FluWatermark::FluWatermark(QQuickItem* parent) : QQuickPaintedItem(parent){ void FluWatermark::paint(QPainter* painter){ QFont font; + font.setFamily(FluTextStyle::getInstance()->family()); font.setPixelSize(_textSize); painter->setFont(font); painter->setPen(_textColor); diff --git a/src/Qt5/imports/FluentUI/Controls/FluAcrylic.qml b/src/Qt5/imports/FluentUI/Controls/FluAcrylic.qml index 5ee5ddfb..66d06fb1 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluAcrylic.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluAcrylic.qml @@ -10,8 +10,7 @@ Item { property real noiseOpacity: 0.02 property alias target: effect_source.sourceItem property int blurRadius: 32 - property rect targetRect: Qt.rect(control.x, control.y, control.width, - control.height) + property rect targetRect: Qt.rect(control.x, control.y, control.width,control.height) ShaderEffectSource { id: effect_source anchors.fill: parent diff --git a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml index 68891d97..3a6d429c 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml @@ -90,7 +90,7 @@ Rectangle { if(!readOnly){ editTextChaged(text_box.text) } - tableView.closeEditor() + control.closeEditor() } } } @@ -119,7 +119,7 @@ Rectangle { if(!readOnly){ editTextChaged(text_box.text) } - tableView.closeEditor() + control.closeEditor() } } } @@ -258,7 +258,7 @@ Rectangle { anchors.fill: parent acceptedButtons: Qt.LeftButton onPressed:{ - closeEditor() + control.closeEditor() } onCanceled: { } @@ -275,7 +275,7 @@ Rectangle { onClicked: (event)=>{ d.current = rowObject - closeEditor() + control.closeEditor() event.accepted = true } } @@ -381,6 +381,7 @@ Rectangle { clip: true onRowsChanged: { control.closeEditor() + table_view.flick(0,1) } delegate: com_table_delegate FluLoader{ diff --git a/src/Qt5/imports/FluentUI/Controls/FluTreeView.qml b/src/Qt5/imports/FluentUI/Controls/FluTreeView.qml index 59e33919..6fb81008 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTreeView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTreeView.qml @@ -5,283 +5,186 @@ import QtQuick.Controls 2.15 import Qt.labs.qmlmodels 1.0 import FluentUI 1.0 -Item { - property int currentIndex : -1 +Rectangle { property var dataSource + property var columnSource : [] property bool showLine: true - property bool draggable: false property int cellHeight: 30 - property int depthPadding: 30 + property int depthPadding: 15 property bool checkable: false - property color lineColor: FluTheme.dark ? Qt.rgba(111/255,111/255,111/255,1) : Qt.rgba(217/255,217/255,217/255,1) + property color lineColor: FluTheme.dividerColor + property color borderColor: FluTheme.dark ? Qt.rgba(37/255,37/255,37/255,1) : Qt.rgba(228/255,228/255,228/255,1) + property color selectedBorderColor: FluTheme.primaryColor + property color selectedColor: FluTools.withOpacity(FluTheme.primaryColor,0.3) + readonly property alias current: d.current id:control - QtObject { - id:d - property int dy - property var current - property int dropIndex: -1 - property bool isDropTopArea: false - property int dragIndex: -1 - property color hitColor: FluTheme.primaryColor - } + color: FluTheme.dark ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1) onDataSourceChanged: { tree_model.setDataSource(dataSource) } + onColumnSourceChanged: { + var columns= [] + var headerRow = {} + columnSource.forEach(function(item){ + var column = Qt.createQmlObject('import Qt.labs.qmlmodels 1.0;TableModelColumn{}',control); + column.display = item.dataIndex + columns.push(column) + headerRow[item.dataIndex] = item.title + }) + header_column_model.columns = columns + header_column_model.rows = [headerRow] + } FluTreeModel{ id:tree_model + columnSource: control.columnSource } - ListView{ - id:table_view - ScrollBar.horizontal: FluScrollBar{} - ScrollBar.vertical: FluScrollBar{} - boundsBehavior: Flickable.StopAtBounds - model: tree_model - anchors.fill: parent - clip: true - flickableDirection: Flickable.HorizontalAndVerticalFlick - contentWidth: contentItem.childrenRect.width - reuseItems: true - removeDisplaced : Transition{ - ParallelAnimation{ - NumberAnimation { - properties: "y" - duration: 167 - from: d.dy + table_view.height - easing.type: Easing.OutCubic - } - NumberAnimation { - properties: "opacity" - duration: 83 - from: 0 - to: 1 - } - } + onDepthPaddingChanged: { + table_view.forceLayout() + } + onCellHeightChanged: { + table_view.forceLayout() + } + onCheckableChanged: { + delay_force_layout.restart() + } + Timer{ + id:delay_force_layout + interval: 30 + onTriggered: { + table_view.forceLayout() } - move: Transition { - NumberAnimation { property: "y"; duration: 200 } - } - add: Transition{ - ParallelAnimation{ - NumberAnimation { - properties: "y" - duration: 167 - from: d.dy - control.cellHeight - easing.type: Easing.OutCubic - } - NumberAnimation { - properties: "opacity" - duration: 83 - from: 0 - to: 1 - } + } + QtObject { + id:d + property var current + property int defaultItemWidth: 100 + property int rowHoverIndex: -1 + property var editDelegate + property var editPosition + function getEditDelegate(column){ + var obj = control.columnSource[column].editDelegate + if(obj){ + return obj } - } - delegate: Item { - id:item_control - implicitWidth: item_loader_container.width - implicitHeight: item_loader_container.height - ListView.onReused: { - item_loader_container.item.reused() + if(control.columnSource[column].editMultiline === true){ + return com_edit_multiline } - ListView.onPooled: { - item_loader_container.item.pooled() - } - FluLoader{ - property var itemControl: item_control - property var itemModel: dataModel - property int rowIndex: index - property bool isItemLoader: true - id:item_loader_container - sourceComponent: com_item_container - } - } - FluLoader{ - id:loader_container - property var itemControl - property var itemModel - property bool isItemLoader: false + return com_edit } } Component{ - id:com_item_container + id:com_edit + FluTextBox{ + id:text_box + text: String(display) + readOnly: true === control.columnSource[column].readOnly + Component.onCompleted: { + forceActiveFocus() + selectAll() + } + onCommit: { + if(!readOnly){ + editTextChaged(text_box.text) + } + control.closeEditor() + } + } + } + Component{ + id:com_edit_multiline Item{ - signal reused - signal pooled - onReused: { - - } - onPooled: { - } - property bool isCurrent: d.current === itemModel - id:item_container - width: { - var w = 46 + item_loader_cell.width + control.depthPadding*itemModel.depth - if(control.width>w){ - return control.width - } - return w - } - height: control.cellHeight - implicitWidth: width - implicitHeight: height - function toggle(){ - var pos = FluTools.cursorPos() - var viewPos = table_view.mapToGlobal(0,0) - d.dy = table_view.contentY + pos.y-viewPos.y - if(itemModel.isExpanded){ - tree_model.collapse(rowIndex) - }else{ - tree_model.expand(rowIndex) + anchors.fill: parent + Flickable{ + id:item_scroll + clip: true + anchors.fill: parent + ScrollBar.vertical: multiline_text_srcoll_bar + boundsBehavior: Flickable.StopAtBounds + TextArea.flickable: FluMultilineTextBox { + id:text_box + text: String(display) + readOnly: true === control.columnSource[column].readOnly + verticalAlignment: TextInput.AlignVCenter + isCtrlEnterForNewline: true + Component.onCompleted: { + forceActiveFocus() + selectAll() + } + rightPadding: 34 + onCommit: { + if(!readOnly){ + editTextChaged(text_box.text) + } + control.closeEditor() + } } } - Rectangle{ - width: 3 - height: 18 - radius: 1.5 - color: FluTheme.primaryColor - visible: isCurrent + FluIconButton{ + iconSource:FluentIcons.ChromeClose + iconSize: 10 + width: 20 + height: 20 + padding: 0 + verticalPadding: 0 + horizontalPadding: 0 + visible: { + if(text_box.readOnly) + return false + return text_box.text !== "" + } anchors{ - left: parent.left - leftMargin: 6 verticalCenter: parent.verticalCenter + right: parent.right + rightMargin: 15 } + onClicked:{ + text_box.text = "" + } + } + FluScrollBar{ + id:multiline_text_srcoll_bar + anchors{ + right: parent.right + rightMargin: 5 + top: parent.top + bottom: parent.bottom + topMargin: 3 + bottomMargin: 3 + } + } + } + } + Component{ + id:com_column + Item{ + id:item_container + clip: true + function toggle(){ + if(rowModel.isExpanded){ + tree_model.collapse(row) + }else{ + tree_model.expand(row) + } + delay_force_layout.restart() } MouseArea{ id:item_mouse property point clickPos: Qt.point(0,0) anchors.fill: parent - drag.target:control.draggable ? loader_container : undefined - hoverEnabled: true - drag.onActiveChanged: { - if(drag.active){ - if(itemModel.isExpanded && itemModel.hasChildren()){ - tree_model.collapse(rowIndex) - } - d.dragIndex = rowIndex - loader_container.sourceComponent = com_item_container - } - } - onPressed: - (mouse)=>{ - clickPos = Qt.point(mouse.x,mouse.y) - loader_container.itemControl = itemControl - loader_container.itemModel = itemModel - var cellPosition = item_container.mapToItem(table_view, 0, 0) - loader_container.width = item_container.width - loader_container.height = item_container.height - loader_container.x = 0 - loader_container.y = cellPosition.y - } onClicked: { - d.current = itemModel + d.current = rowModel } onDoubleClicked: { - if(itemModel.hasChildren()){ + if(rowModel.hasChildren()){ item_container.toggle() } } - onPositionChanged: - (mouse)=> { - if(!drag.active){ - return - } - var cellPosition = item_container.mapToItem(table_view, 0, 0) - if(mouse.y+cellPosition.y<0 || mouse.y+cellPosition.y>table_view.height){ - d.dropIndex = -1 - return - } - if((mouse.x-table_view.contentX)>table_view.width || (mouse.x-table_view.contentX)<0){ - d.dropIndex = -1 - return - } - var pos = FluTools.cursorPos() - var viewPos = table_view.mapToGlobal(0,0) - var y = table_view.contentY + pos.y-viewPos.y - var index = Math.floor(y/control.cellHeight) - if(index<0 || index>table_view.count-1){ - d.dropIndex = -1 - return - } - if(tree_model.hitHasChildrenExpanded(index) && y>index*control.cellHeight+control.cellHeight/2){ - d.dropIndex = index + 1 - d.isDropTopArea = true - }else{ - d.dropIndex = index - if(y>index*control.cellHeight+control.cellHeight/2){ - d.isDropTopArea = false - }else{ - d.isDropTopArea = true - } - } - } - onCanceled: { - loader_container.sourceComponent = undefined - loader_container.x = 0 - loader_container.y = 0 - d.dropIndex = -1 - d.dragIndex = -1 - } - onReleased: { - loader_container.sourceComponent = undefined - if(d.dropIndex !== -1){ - tree_model.dragAndDrop(d.dragIndex,d.dropIndex,d.isDropTopArea) - } - d.dropIndex = -1 - d.dragIndex = -1 - loader_container.x = 0 - loader_container.y = 0 - } - } - Drag.active: item_mouse.drag.active - Rectangle{ - id:item_line_drop_tip - anchors{ - left: layout_row.left - leftMargin: 26 - right: parent.right - rightMargin: 10 - bottom: parent.bottom - bottomMargin: -1.5 - top: undefined - } - states: [ - State { - when:d.isDropTopArea - AnchorChanges { - target: item_line_drop_tip - anchors.top: item_container.top - anchors.bottom: undefined - } - PropertyChanges { - target: item_line_drop_tip - anchors.topMargin: -1.5 - } - } - ] - height: 3 - radius: 1.5 - color: d.hitColor - visible: d.dropIndex === rowIndex - Rectangle{ - width: 10 - height: 10 - radius: 5 - border.width: 3 - border.color: d.hitColor - color: FluTheme.dark ? FluColors.Black : FluColors.White - anchors{ - top: parent.top - left: parent.left - topMargin: -3 - leftMargin: -5 - } - } } FluRectangle{ width: 1 color: control.lineColor - visible: control.showLine && isItemLoader && itemModel.depth !== 0 && !itemModel.hasChildren() - height: itemModel.hideLineFooter() ? parent.height/2 : parent.height + visible: control.showLine && rowModel.depth !== 0 && !rowModel.hasChildren() + height: rowModel.hideLineFooter() ? parent.height/2 : parent.height anchors{ top: parent.top left: item_line_h.left @@ -291,7 +194,7 @@ Item { id:item_line_h height: 1 color: control.lineColor - visible: control.showLine && isItemLoader && itemModel.depth !== 0 && !itemModel.hasChildren() + visible: control.showLine && rowModel.depth !== 0 && !rowModel.hasChildren() width: depthPadding - 10 anchors{ right: layout_row.left @@ -300,12 +203,12 @@ Item { } } Repeater{ - model: Math.max(itemModel.depth-1,0) + model: Math.max(rowModel.depth-1,0) delegate: FluRectangle{ required property int index width: 1 color: control.lineColor - visible: control.showLine && isItemLoader && itemModel.depth !== 0 && itemModel.hasNextNodeByIndex(index) + visible: control.showLine && rowModel.depth !== 0 && rowModel.hasNextNodeByIndex(index) anchors{ top:parent.top bottom: parent.bottom @@ -314,57 +217,34 @@ Item { } } } - Rectangle{ - anchors.fill: parent - radius: 4 - anchors.leftMargin: 6 - anchors.rightMargin: 6 - border.color: d.hitColor - border.width: d.dragIndex === rowIndex ? 1 : 0 - color: { - if(isCurrent){ - return FluTheme.itemCheckColor - } - if(item_mouse.containsMouse || item_check_box.hovered){ - return FluTheme.itemHoverColor - } - if(item_loader_expand.item && item_loader_expand.item.hovered){ - return FluTheme.itemHoverColor - } - return FluTheme.itemNormalColor - } - } RowLayout{ id:layout_row height: parent.height - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left + anchors.fill: parent spacing: 0 - anchors.leftMargin: 14 + control.depthPadding*itemModel.depth + anchors.leftMargin: 14 + control.depthPadding*rowModel.depth Component{ id:com_icon_btn FluIconButton{ - opacity: itemModel.hasChildren() + opacity: rowModel.hasChildren() onClicked: { item_container.toggle() } contentItem:FluIcon{ - rotation: itemModel.isExpanded?0:-90 + rotation: rowModel.isExpanded?0:-90 iconSource:FluentIcons.ChevronDown iconSize: 16 anchors.centerIn: parent } } } - FluLoader{ id:item_loader_expand Layout.preferredWidth: 20 Layout.preferredHeight: 20 - sourceComponent: itemModel.hasChildren() ? com_icon_btn : undefined + sourceComponent: rowModel.hasChildren() ? com_icon_btn : undefined Layout.alignment: Qt.AlignVCenter } - FluCheckBox{ id:item_check_box Layout.preferredWidth: 18 @@ -372,39 +252,453 @@ Item { Layout.leftMargin: 5 horizontalPadding:0 verticalPadding: 0 - checked: itemModel.checked + checked: rowModel.checked animationEnabled:false visible: control.checkable padding: 0 clickListener: function(){ - tree_model.checkRow(rowIndex,!itemModel.checked) + tree_model.checkRow(row,!rowModel.checked) } Layout.alignment: Qt.AlignVCenter } - FluLoader{ - property var dataModel: itemModel - property var itemMouse: item_mouse - id:item_loader_cell - Layout.leftMargin: 10 - Layout.preferredWidth: { - if(item){ - return item.width - } - return 0 - } + Item{ Layout.fillHeight: true - sourceComponent:com_item_text + Layout.fillWidth: true + Layout.leftMargin: 6 + FluText { + id:item_text + text: String(display) + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + anchors.fill: parent + MouseArea{ + acceptedButtons: Qt.NoButton + id: hover_handler + hoverEnabled: true + anchors.fill: parent + } + FluTooltip{ + text: item_text.text + delay: 500 + visible: item_text.contentWidth < item_text.implicitWidth && hover_handler.containsMouse + } + } } + } } } + Component{ + id:com_other + FluText { + id:item_text + text: String(display) + elide: Text.ElideRight + wrapMode: Text.WrapAnywhere + anchors{ + fill: parent + leftMargin: 11 + rightMargin: 11 + topMargin: 6 + bottomMargin: 6 + } + verticalAlignment: Text.AlignVCenter + MouseArea{ + acceptedButtons: Qt.NoButton + id: hover_handler + hoverEnabled: true + anchors.fill: parent + } + FluTooltip{ + text: item_text.text + delay: 500 + visible: item_text.contentWidth < item_text.implicitWidth && item_text.contentHeight < item_text.implicitHeight && hover_handler.containsMouse + } + } + } + TableView{ + id:table_view + ScrollBar.horizontal: FluScrollBar{} + ScrollBar.vertical: FluScrollBar{} + boundsBehavior: Flickable.StopAtBounds + model: tree_model + anchors{ + top: header_horizontal.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + } + clip: true + columnWidthProvider: function(column) { + var columnObject = control.columnSource[column] + var width = columnObject.width + if(width){ + return width + } + var minimumWidth = columnObject.minimumWidth + if(minimumWidth){ + return minimumWidth + } + return d.defaultItemWidth + } + rowHeightProvider: function(row) { + return control.cellHeight + } + delegate: MouseArea{ + property var rowObject : rowModel.data + property alias isRowSelected: item_table_loader.isRowSelected + property var display: rowModel.data[columnModel.dataIndex] + property bool isObject: typeof(item_table.display) == "object" + property bool editVisible: { + if(rowObject && d.editPosition && d.editPosition._key === rowObject._key && d.editPosition.column === column){ + return true + } + return false + } + implicitHeight: 30 + implicitWidth: 30 + id: item_table + hoverEnabled: true + onEntered: { + d.rowHoverIndex = row + } + onWidthChanged: { + if(editVisible){ + updateEditPosition() + } + } + onHeightChanged: { + if(editVisible){ + updateEditPosition() + } + } + onXChanged: { + if(editVisible){ + updateEditPosition() + } + } + onYChanged: { + if(editVisible){ + updateEditPosition() + } + } + function updateEditPosition(){ + var obj = {} + obj._key = rowObject._key + obj.column = column + obj.row = row + obj.x = item_table.x + obj.y = item_table.y + 1 + obj.width = item_table.width + obj.height = item_table.height - 2 + d.editPosition = obj + } + onDoubleClicked:{ + if(typeof(display) == "object"){ + return + } + d.editDelegate = d.getEditDelegate(column) + updateEditPosition() + loader_edit.display = display + } + onClicked: + (event)=>{ + d.current = rowModel + event.accepted = true + } + Rectangle{ + anchors.fill: parent + color:{ + if(item_table.isRowSelected){ + return control.selectedColor + } + if(d.rowHoverIndex === row || item_table.isRowSelected){ + return FluTheme.dark ? Qt.rgba(1,1,1,0.06) : Qt.rgba(0,0,0,0.06) + } + return (row%2!==0) ? control.color : (FluTheme.dark ? Qt.rgba(1,1,1,0.015) : Qt.rgba(0,0,0,0.015)) + } + Item{ + anchors.fill: parent + visible: item_table.isRowSelected + Rectangle{ + width: 1 + height: parent.height + anchors.left: parent.left + color: control.selectedBorderColor + visible: column === 0 + } + Rectangle{ + width: 1 + height: parent.height + anchors.right: parent.right + color: control.selectedBorderColor + visible: column === control.columnSource.length-1 + } + Rectangle{ + width: parent.width + height: 1 + anchors.top: parent.top + color: control.selectedBorderColor + } + Rectangle{ + width: parent.width + height: 1 + anchors.bottom: parent.bottom + color: control.selectedBorderColor + } + } + } + FluLoader{ + anchors.fill: parent + id:item_table_loader + property var rowModel : model.rowModel + property var columnModel : model.columnModel + property int row : model.row + property int column: model.column + property var display: item_table.display + property bool isRowSelected: d.current === rowModel + property var options: { + if(isObject){ + return display.options + } + return {} + } + sourceComponent: { + if(column === 0) + return com_column + if(item_table.isObject){ + return item_table.display.comId + } + return com_other + } + } + } + FluLoader{ + id:loader_edit + property var tableView: control + property var display + property int column: { + if(d.editPosition){ + return d.editPosition.column + } + return 0 + } + property int row: { + if(d.editPosition){ + return d.editPosition.row + } + return 0 + } + signal editTextChaged(string text) + sourceComponent: d.editPosition ? d.editDelegate : undefined + onEditTextChaged: + (text)=>{ + const obj = tree_model.getRow(row).data + obj[control.columnSource[column].dataIndex] = text + tree_model.setRow(row,obj) + } + width: { + if(d.editPosition){ + return d.editPosition.width + } + return 0 + } + height: { + if(d.editPosition){ + return d.editPosition.height + } + return 0 + } + x:{ + if(d.editPosition){ + return d.editPosition.x + } + return 0 + } + y:{ + if(d.editPosition){ + return d.editPosition.y + } + return 0 + } + z:999 + } + } + TableModel{ + id:header_column_model + TableModelColumn {} + } + Component{ + id:com_column_text + FluText { + id: column_text + text: modelData + anchors.fill: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + Component{ + id:com_column_header_delegate + Rectangle{ + id:column_item_control + readonly property real cellPadding: 8 + property bool canceled: false + property int columnIndex: column + property var columnObject : control.columnSource[column] + implicitWidth: { + return (item_column_loader.item && item_column_loader.item.implicitWidth) + (cellPadding * 2) + } + implicitHeight: Math.max(36, (item_column_loader.item&&item_column_loader.item.implicitHeight) + (cellPadding * 2)) + color: FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(247/255,247/255,247/255,1) + Rectangle{ + border.color: control.borderColor + width: parent.width + height: 1 + anchors.top: parent.top + color:"#00000000" + } + Rectangle{ + border.color: control.borderColor + width: parent.width + height: 1 + anchors.bottom: parent.bottom + color:"#00000000" + } + Rectangle{ + border.color: control.borderColor + width: 1 + height: parent.height + anchors.left: parent.left + color:"#00000000" + } + Rectangle{ + border.color: control.borderColor + width: 1 + height: parent.height + anchors.right: parent.right + color:"#00000000" + visible: column === table_view.columns - 1 + } + MouseArea{ + id:column_item_control_mouse + anchors.fill: parent + anchors.rightMargin: 6 + hoverEnabled: true + onCanceled: { + column_item_control.canceled = true + } + onContainsMouseChanged: { + if(!containsMouse){ + column_item_control.canceled = false + } + } + onClicked: + (event)=>{ + + } + } + FluLoader{ + id:item_column_loader + property var itemModel: model + property var modelData: model.display + property var tableView: table_view + property var options:{ + if(typeof(modelData) == "object"){ + return modelData.options + } + return {} + } + property int column: column_item_control.columnIndex + width: parent.width + height: parent.height + sourceComponent: { + if(typeof(modelData) == "object"){ + return modelData.comId + } + return com_column_text + } + } + MouseArea{ + property point clickPos: "0,0" + height: parent.height + width: 6 + anchors.right: parent.right + acceptedButtons: Qt.LeftButton + hoverEnabled: true + visible: !(columnObject.width === columnObject.minimumWidth && columnObject.width === columnObject.maximumWidth && columnObject.width) + cursorShape: Qt.SplitHCursor + preventStealing: true + onPressed : + (mouse)=>{ + FluTools.setOverrideCursor(Qt.SplitHCursor) + clickPos = Qt.point(mouse.x, mouse.y) + } + onReleased:{ + FluTools.restoreOverrideCursor() + } + onCanceled: { + FluTools.restoreOverrideCursor() + } + onPositionChanged: + (mouse)=>{ + if(!pressed){ + return + } + var delta = Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y) + var minimumWidth = columnObject.minimumWidth + var maximumWidth = columnObject.maximumWidth + var w = columnObject.width + if(!w){ + w = d.defaultItemWidth + } + if(!minimumWidth){ + minimumWidth = d.defaultItemWidth + } + if(!maximumWidth){ + maximumWidth = 65535 + } + columnObject.width = Math.min(Math.max(minimumWidth, w + delta.x),maximumWidth) + table_view.forceLayout() + } + } + } + } + + TableView { + id: header_horizontal + model: header_column_model + anchors{ + left: table_view.left + right: table_view.right + top: parent.top + } + height: Math.max(1, contentHeight) + boundsBehavior: Flickable.StopAtBounds + clip: true + syncDirection: Qt.Horizontal + columnWidthProvider: table_view.columnWidthProvider + syncView: table_view.rows === 0 ? null : table_view + onContentXChanged:{ + timer_horizontal_force_layout.restart() + } + Timer{ + id:timer_horizontal_force_layout + interval: 50 + onTriggered: { + header_horizontal.forceLayout() + } + } + delegate: com_column_header_delegate + } + Component{ id:com_item_text Item{ width: item_text.width FluText { id:item_text - text: dataModel.title + text: model.title rightPadding: 14 anchors.centerIn: parent color:{ @@ -423,7 +717,7 @@ Item { return tree_model.dataSourceSize } function visibleCount(){ - return table_view.count + return table_view.rows } function collapse(rowIndex){ tree_model.collapse(rowIndex) @@ -437,4 +731,14 @@ Item { function allCollapse(){ tree_model.allCollapse() } + function customItem(comId,options={}){ + var o = {} + o.comId = comId + o.options = options + return o + } + function closeEditor(){ + d.editPosition = undefined + d.editDelegate = undefined + } } diff --git a/src/Qt5/imports/FluentUI/plugins.qmltypes b/src/Qt5/imports/FluentUI/plugins.qmltypes index c1bda21d..365b380d 100644 --- a/src/Qt5/imports/FluentUI/plugins.qmltypes +++ b/src/Qt5/imports/FluentUI/plugins.qmltypes @@ -21,6 +21,24 @@ Module { Property { name: "lighter"; type: "QColor" } Property { name: "lightest"; type: "QColor" } } + Component { + name: "FluApp" + prototype: "QObject" + exports: ["FluentUI/FluApp 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "useSystemAppBar"; type: "bool" } + Property { name: "windowIcon"; type: "string" } + Property { name: "locale"; type: "QLocale" } + Method { + name: "init" + Parameter { name: "target"; type: "QObject"; isPointer: true } + Parameter { name: "locale"; type: "QLocale" } + } + Method { + name: "init" + Parameter { name: "target"; type: "QObject"; isPointer: true } + } + } Component { name: "FluCalendarViewType" exports: ["FluentUI/FluCalendarViewType 1.0"] @@ -50,6 +68,50 @@ Module { Parameter { name: "code"; type: "string" } } } + Component { + name: "FluColors" + prototype: "QObject" + exports: ["FluentUI/FluColors 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "Transparent"; type: "QColor" } + Property { name: "Black"; type: "QColor" } + Property { name: "White"; type: "QColor" } + Property { name: "Grey10"; type: "QColor" } + Property { name: "Grey20"; type: "QColor" } + Property { name: "Grey30"; type: "QColor" } + Property { name: "Grey40"; type: "QColor" } + Property { name: "Grey50"; type: "QColor" } + Property { name: "Grey60"; type: "QColor" } + Property { name: "Grey70"; type: "QColor" } + Property { name: "Grey80"; type: "QColor" } + Property { name: "Grey90"; type: "QColor" } + Property { name: "Grey100"; type: "QColor" } + Property { name: "Grey110"; type: "QColor" } + Property { name: "Grey120"; type: "QColor" } + Property { name: "Grey130"; type: "QColor" } + Property { name: "Grey140"; type: "QColor" } + Property { name: "Grey150"; type: "QColor" } + Property { name: "Grey160"; type: "QColor" } + Property { name: "Grey170"; type: "QColor" } + Property { name: "Grey180"; type: "QColor" } + Property { name: "Grey190"; type: "QColor" } + Property { name: "Grey200"; type: "QColor" } + Property { name: "Grey210"; type: "QColor" } + Property { name: "Grey220"; type: "QColor" } + Property { name: "Yellow"; type: "FluAccentColor"; isPointer: true } + Property { name: "Orange"; type: "FluAccentColor"; isPointer: true } + Property { name: "Red"; type: "FluAccentColor"; isPointer: true } + Property { name: "Magenta"; type: "FluAccentColor"; isPointer: true } + Property { name: "Purple"; type: "FluAccentColor"; isPointer: true } + Property { name: "Blue"; type: "FluAccentColor"; isPointer: true } + Property { name: "Teal"; type: "FluAccentColor"; isPointer: true } + Property { name: "Green"; type: "FluAccentColor"; isPointer: true } + Method { + name: "createAccentColor" + type: "FluAccentColor*" + Parameter { name: "primaryColor"; type: "QColor" } + } + } Component { name: "FluContentDialogType" exports: ["FluentUI/FluContentDialogType 1.0"] @@ -226,6 +288,49 @@ Module { Parameter { name: "filter"; type: "QJSValue" } } } + Component { + name: "FluTextStyle" + prototype: "QObject" + exports: ["FluentUI/FluTextStyle 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "family"; type: "string" } + Property { name: "Caption"; type: "QFont" } + Property { name: "Body"; type: "QFont" } + Property { name: "BodyStrong"; type: "QFont" } + Property { name: "Subtitle"; type: "QFont" } + Property { name: "Title"; type: "QFont" } + Property { name: "TitleLarge"; type: "QFont" } + Property { name: "Display"; type: "QFont" } + } + Component { + name: "FluTheme" + prototype: "QObject" + exports: ["FluentUI/FluTheme 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "dark"; type: "bool"; isReadonly: true } + Property { name: "accentColor"; type: "FluAccentColor"; isPointer: true } + Property { name: "primaryColor"; type: "QColor" } + Property { name: "backgroundColor"; type: "QColor" } + Property { name: "dividerColor"; type: "QColor" } + Property { name: "windowBackgroundColor"; type: "QColor" } + Property { name: "windowActiveBackgroundColor"; type: "QColor" } + Property { name: "fontPrimaryColor"; type: "QColor" } + Property { name: "fontSecondaryColor"; type: "QColor" } + Property { name: "fontTertiaryColor"; type: "QColor" } + Property { name: "itemNormalColor"; type: "QColor" } + Property { name: "itemHoverColor"; type: "QColor" } + Property { name: "itemPressColor"; type: "QColor" } + Property { name: "itemCheckColor"; type: "QColor" } + Property { name: "darkMode"; type: "int" } + Property { name: "nativeText"; type: "bool" } + Property { name: "animationEnabled"; type: "bool" } + Method { + name: "awesomeList" + type: "QJsonArray" + Parameter { name: "keyword"; type: "string" } + } + Method { name: "awesomeList"; type: "QJsonArray" } + } Component { name: "FluThemeType" exports: ["FluentUI/FluThemeType 1.0"] @@ -267,6 +372,115 @@ Module { } } } + Component { + name: "FluTools" + prototype: "QObject" + exports: ["FluentUI/FluTools 1.0"] + exportMetaObjectRevisions: [0] + Method { name: "qtMajor"; type: "int" } + Method { name: "qtMinor"; type: "int" } + Method { name: "isMacos"; type: "bool" } + Method { name: "isLinux"; type: "bool" } + Method { name: "isWin"; type: "bool" } + Method { + name: "clipText" + Parameter { name: "text"; type: "string" } + } + Method { name: "uuid"; type: "string" } + Method { + name: "readFile" + type: "string" + Parameter { name: "fileName"; type: "string" } + } + Method { + name: "setQuitOnLastWindowClosed" + Parameter { name: "val"; type: "bool" } + } + Method { + name: "setOverrideCursor" + Parameter { name: "shape"; type: "Qt::CursorShape" } + } + Method { name: "restoreOverrideCursor" } + Method { + name: "html2PlantText" + type: "string" + Parameter { name: "html"; type: "string" } + } + Method { + name: "toLocalPath" + type: "string" + Parameter { name: "url"; type: "QUrl" } + } + Method { + name: "deleteLater" + Parameter { name: "p"; type: "QObject"; isPointer: true } + } + Method { + name: "getFileNameByUrl" + type: "string" + Parameter { name: "url"; type: "QUrl" } + } + Method { name: "getVirtualGeometry"; type: "QRect" } + Method { name: "getApplicationDirPath"; type: "string" } + Method { + name: "getUrlByFilePath" + type: "QUrl" + Parameter { name: "path"; type: "string" } + } + Method { + name: "withOpacity" + type: "QColor" + Parameter { type: "QColor" } + Parameter { name: "alpha"; type: "double" } + } + Method { + name: "md5" + type: "string" + Parameter { name: "text"; type: "string" } + } + Method { + name: "sha256" + type: "string" + Parameter { name: "text"; type: "string" } + } + Method { + name: "toBase64" + type: "string" + Parameter { name: "text"; type: "string" } + } + Method { + name: "fromBase64" + type: "string" + Parameter { name: "text"; type: "string" } + } + Method { + name: "removeDir" + type: "bool" + Parameter { name: "dirPath"; type: "string" } + } + Method { + name: "removeFile" + type: "bool" + Parameter { name: "filePath"; type: "string" } + } + Method { + name: "showFileInFolder" + Parameter { name: "path"; type: "string" } + } + Method { name: "isSoftware"; type: "bool" } + Method { name: "currentTimestamp"; type: "qlonglong" } + Method { name: "cursorPos"; type: "QPoint" } + Method { name: "windowIcon"; type: "QIcon" } + Method { name: "cursorScreenIndex"; type: "int" } + Method { name: "windowBuildNumber"; type: "int" } + Method { name: "isWindows11OrGreater"; type: "bool" } + Method { name: "isWindows10OrGreater"; type: "bool" } + Method { + name: "desktopAvailableGeometry" + type: "QRect" + Parameter { name: "window"; type: "QQuickWindow"; isPointer: true } + } + } Component { name: "FluTreeModel" prototype: "QAbstractItemModel" @@ -274,6 +488,7 @@ Module { exportMetaObjectRevisions: [0] Property { name: "dataSourceSize"; type: "int" } Property { name: "selectionModel"; type: "QList" } + Property { name: "columnSource"; type: "QList" } Method { name: "removeRows" Parameter { name: "row"; type: "int" } @@ -305,12 +520,6 @@ Module { name: "expand" Parameter { name: "row"; type: "int" } } - Method { - name: "dragAndDrop" - Parameter { name: "dragIndex"; type: "int" } - Parameter { name: "dropIndex"; type: "int" } - Parameter { name: "isDropTopArea"; type: "bool" } - } Method { name: "getNode" type: "FluTreeNode*" @@ -2242,7 +2451,7 @@ Module { } Property { name: "layoutMacosButtons" - type: "FluLoader_QMLTYPE_13" + type: "FluLoader_QMLTYPE_12" isReadonly: true isPointer: true } @@ -2932,15 +3141,15 @@ Module { defaultProperty: "data" Property { name: "logo"; type: "QUrl" } Property { name: "title"; type: "string" } - Property { name: "items"; type: "FluObject_QMLTYPE_137"; isPointer: true } - Property { name: "footerItems"; type: "FluObject_QMLTYPE_137"; isPointer: true } + Property { name: "items"; type: "FluObject_QMLTYPE_172"; isPointer: true } + Property { name: "footerItems"; type: "FluObject_QMLTYPE_172"; isPointer: true } Property { name: "displayMode"; type: "int" } Property { name: "autoSuggestBox"; type: "QQmlComponent"; isPointer: true } Property { name: "actionItem"; type: "QQmlComponent"; isPointer: true } Property { name: "topPadding"; type: "int" } Property { name: "pageMode"; type: "int" } - Property { name: "navItemRightMenu"; type: "FluMenu_QMLTYPE_38"; isPointer: true } - Property { name: "navItemExpanderRightMenu"; type: "FluMenu_QMLTYPE_38"; isPointer: true } + Property { name: "navItemRightMenu"; type: "FluMenu_QMLTYPE_42"; isPointer: true } + Property { name: "navItemExpanderRightMenu"; type: "FluMenu_QMLTYPE_42"; isPointer: true } Property { name: "navCompactWidth"; type: "int" } Property { name: "navTopMargin"; type: "int" } Property { name: "cellHeight"; type: "int" } diff --git a/src/Qt6/imports/FluentUI/Controls/FluAcrylic.qml b/src/Qt6/imports/FluentUI/Controls/FluAcrylic.qml index 035fe423..7a76e91f 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluAcrylic.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluAcrylic.qml @@ -10,8 +10,7 @@ Item { property real noiseOpacity: 0.02 property alias target: effect_source.sourceItem property int blurRadius: 32 - property rect targetRect: Qt.rect(control.x, control.y, control.width, - control.height) + property rect targetRect: Qt.rect(control.x, control.y, control.width,control.height) ShaderEffectSource { id: effect_source anchors.fill: parent diff --git a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml index 4ff45c9e..a8634eb6 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml @@ -91,7 +91,7 @@ Rectangle { if(!readOnly){ editTextChaged(text_box.text) } - tableView.closeEditor() + control.closeEditor() } } } @@ -120,7 +120,7 @@ Rectangle { if(!readOnly){ editTextChaged(text_box.text) } - tableView.closeEditor() + control.closeEditor() } } } @@ -259,7 +259,7 @@ Rectangle { anchors.fill: parent acceptedButtons: Qt.LeftButton onPressed:{ - closeEditor() + control.closeEditor() } onCanceled: { } @@ -276,7 +276,7 @@ Rectangle { onClicked: (event)=>{ d.current = rowObject - closeEditor() + control.closeEditor() event.accepted = true } } @@ -382,6 +382,7 @@ Rectangle { clip: true onRowsChanged: { control.closeEditor() + table_view.flick(0,1) } delegate: com_table_delegate FluLoader{ diff --git a/src/Qt6/imports/FluentUI/Controls/FluTreeView.qml b/src/Qt6/imports/FluentUI/Controls/FluTreeView.qml index e08e5dbd..d698deea 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluTreeView.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluTreeView.qml @@ -5,283 +5,186 @@ import QtQuick.Controls import Qt.labs.qmlmodels import FluentUI -Item { - property int currentIndex : -1 +Rectangle { property var dataSource + property var columnSource : [] property bool showLine: true - property bool draggable: false property int cellHeight: 30 - property int depthPadding: 30 + property int depthPadding: 15 property bool checkable: false - property color lineColor: FluTheme.dark ? Qt.rgba(111/255,111/255,111/255,1) : Qt.rgba(217/255,217/255,217/255,1) + property color lineColor: FluTheme.dividerColor + property color borderColor: FluTheme.dark ? Qt.rgba(37/255,37/255,37/255,1) : Qt.rgba(228/255,228/255,228/255,1) + property color selectedBorderColor: FluTheme.primaryColor + property color selectedColor: FluTools.withOpacity(FluTheme.primaryColor,0.3) + readonly property alias current: d.current id:control - QtObject { - id:d - property int dy - property var current - property int dropIndex: -1 - property bool isDropTopArea: false - property int dragIndex: -1 - property color hitColor: FluTheme.primaryColor - } + color: FluTheme.dark ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1) onDataSourceChanged: { tree_model.setDataSource(dataSource) } + onColumnSourceChanged: { + var columns= [] + var headerRow = {} + columnSource.forEach(function(item){ + var column = Qt.createQmlObject('import Qt.labs.qmlmodels 1.0;TableModelColumn{}',control); + column.display = item.dataIndex + columns.push(column) + headerRow[item.dataIndex] = item.title + }) + header_column_model.columns = columns + header_column_model.rows = [headerRow] + } FluTreeModel{ id:tree_model + columnSource: control.columnSource } - ListView{ - id:table_view - ScrollBar.horizontal: FluScrollBar{} - ScrollBar.vertical: FluScrollBar{} - boundsBehavior: Flickable.StopAtBounds - model: tree_model - anchors.fill: parent - clip: true - flickableDirection: Flickable.HorizontalAndVerticalFlick - contentWidth: contentItem.childrenRect.width - reuseItems: true - removeDisplaced : Transition{ - ParallelAnimation{ - NumberAnimation { - properties: "y" - duration: 167 - from: d.dy + table_view.height - easing.type: Easing.OutCubic - } - NumberAnimation { - properties: "opacity" - duration: 83 - from: 0 - to: 1 - } - } + onDepthPaddingChanged: { + table_view.forceLayout() + } + onCellHeightChanged: { + table_view.forceLayout() + } + onCheckableChanged: { + delay_force_layout.restart() + } + Timer{ + id:delay_force_layout + interval: 30 + onTriggered: { + table_view.forceLayout() } - move: Transition { - NumberAnimation { property: "y"; duration: 200 } - } - add: Transition{ - ParallelAnimation{ - NumberAnimation { - properties: "y" - duration: 167 - from: d.dy - control.cellHeight - easing.type: Easing.OutCubic - } - NumberAnimation { - properties: "opacity" - duration: 83 - from: 0 - to: 1 - } + } + QtObject { + id:d + property var current + property int defaultItemWidth: 100 + property int rowHoverIndex: -1 + property var editDelegate + property var editPosition + function getEditDelegate(column){ + var obj = control.columnSource[column].editDelegate + if(obj){ + return obj } - } - delegate: Item { - id:item_control - implicitWidth: item_loader_container.width - implicitHeight: item_loader_container.height - ListView.onReused: { - item_loader_container.item.reused() + if(control.columnSource[column].editMultiline === true){ + return com_edit_multiline } - ListView.onPooled: { - item_loader_container.item.pooled() - } - FluLoader{ - property var itemControl: item_control - property var itemModel: dataModel - property int rowIndex: index - property bool isItemLoader: true - id:item_loader_container - sourceComponent: com_item_container - } - } - FluLoader{ - id:loader_container - property var itemControl - property var itemModel - property bool isItemLoader: false + return com_edit } } Component{ - id:com_item_container + id:com_edit + FluTextBox{ + id:text_box + text: String(display) + readOnly: true === control.columnSource[column].readOnly + Component.onCompleted: { + forceActiveFocus() + selectAll() + } + onCommit: { + if(!readOnly){ + editTextChaged(text_box.text) + } + control.closeEditor() + } + } + } + Component{ + id:com_edit_multiline Item{ - signal reused - signal pooled - onReused: { - - } - onPooled: { - } - property bool isCurrent: d.current === itemModel - id:item_container - width: { - var w = 46 + item_loader_cell.width + control.depthPadding*itemModel.depth - if(control.width>w){ - return control.width - } - return w - } - height: control.cellHeight - implicitWidth: width - implicitHeight: height - function toggle(){ - var pos = FluTools.cursorPos() - var viewPos = table_view.mapToGlobal(0,0) - d.dy = table_view.contentY + pos.y-viewPos.y - if(itemModel.isExpanded){ - tree_model.collapse(rowIndex) - }else{ - tree_model.expand(rowIndex) + anchors.fill: parent + Flickable{ + id:item_scroll + clip: true + anchors.fill: parent + ScrollBar.vertical: multiline_text_srcoll_bar + boundsBehavior: Flickable.StopAtBounds + TextArea.flickable: FluMultilineTextBox { + id:text_box + text: String(display) + readOnly: true === control.columnSource[column].readOnly + verticalAlignment: TextInput.AlignVCenter + isCtrlEnterForNewline: true + Component.onCompleted: { + forceActiveFocus() + selectAll() + } + rightPadding: 34 + onCommit: { + if(!readOnly){ + editTextChaged(text_box.text) + } + control.closeEditor() + } } } - Rectangle{ - width: 3 - height: 18 - radius: 1.5 - color: FluTheme.primaryColor - visible: isCurrent + FluIconButton{ + iconSource:FluentIcons.ChromeClose + iconSize: 10 + width: 20 + height: 20 + padding: 0 + verticalPadding: 0 + horizontalPadding: 0 + visible: { + if(text_box.readOnly) + return false + return text_box.text !== "" + } anchors{ - left: parent.left - leftMargin: 6 verticalCenter: parent.verticalCenter + right: parent.right + rightMargin: 15 } + onClicked:{ + text_box.text = "" + } + } + FluScrollBar{ + id:multiline_text_srcoll_bar + anchors{ + right: parent.right + rightMargin: 5 + top: parent.top + bottom: parent.bottom + topMargin: 3 + bottomMargin: 3 + } + } + } + } + Component{ + id:com_column + Item{ + id:item_container + clip: true + function toggle(){ + if(rowModel.isExpanded){ + tree_model.collapse(row) + }else{ + tree_model.expand(row) + } + delay_force_layout.restart() } MouseArea{ id:item_mouse property point clickPos: Qt.point(0,0) anchors.fill: parent - drag.target:control.draggable ? loader_container : undefined - hoverEnabled: true - drag.onActiveChanged: { - if(drag.active){ - if(itemModel.isExpanded && itemModel.hasChildren()){ - tree_model.collapse(rowIndex) - } - d.dragIndex = rowIndex - loader_container.sourceComponent = com_item_container - } - } - onPressed: - (mouse)=>{ - clickPos = Qt.point(mouse.x,mouse.y) - loader_container.itemControl = itemControl - loader_container.itemModel = itemModel - var cellPosition = item_container.mapToItem(table_view, 0, 0) - loader_container.width = item_container.width - loader_container.height = item_container.height - loader_container.x = 0 - loader_container.y = cellPosition.y - } onClicked: { - d.current = itemModel + d.current = rowModel } onDoubleClicked: { - if(itemModel.hasChildren()){ + if(rowModel.hasChildren()){ item_container.toggle() } } - onPositionChanged: - (mouse)=> { - if(!drag.active){ - return - } - var cellPosition = item_container.mapToItem(table_view, 0, 0) - if(mouse.y+cellPosition.y<0 || mouse.y+cellPosition.y>table_view.height){ - d.dropIndex = -1 - return - } - if((mouse.x-table_view.contentX)>table_view.width || (mouse.x-table_view.contentX)<0){ - d.dropIndex = -1 - return - } - var pos = FluTools.cursorPos() - var viewPos = table_view.mapToGlobal(0,0) - var y = table_view.contentY + pos.y-viewPos.y - var index = Math.floor(y/control.cellHeight) - if(index<0 || index>table_view.count-1){ - d.dropIndex = -1 - return - } - if(tree_model.hitHasChildrenExpanded(index) && y>index*control.cellHeight+control.cellHeight/2){ - d.dropIndex = index + 1 - d.isDropTopArea = true - }else{ - d.dropIndex = index - if(y>index*control.cellHeight+control.cellHeight/2){ - d.isDropTopArea = false - }else{ - d.isDropTopArea = true - } - } - } - onCanceled: { - loader_container.sourceComponent = undefined - loader_container.x = 0 - loader_container.y = 0 - d.dropIndex = -1 - d.dragIndex = -1 - } - onReleased: { - loader_container.sourceComponent = undefined - if(d.dropIndex !== -1){ - tree_model.dragAndDrop(d.dragIndex,d.dropIndex,d.isDropTopArea) - } - d.dropIndex = -1 - d.dragIndex = -1 - loader_container.x = 0 - loader_container.y = 0 - } - } - Drag.active: item_mouse.drag.active - Rectangle{ - id:item_line_drop_tip - anchors{ - left: layout_row.left - leftMargin: 26 - right: parent.right - rightMargin: 10 - bottom: parent.bottom - bottomMargin: -1.5 - top: undefined - } - states: [ - State { - when:d.isDropTopArea - AnchorChanges { - target: item_line_drop_tip - anchors.top: item_container.top - anchors.bottom: undefined - } - PropertyChanges { - target: item_line_drop_tip - anchors.topMargin: -1.5 - } - } - ] - height: 3 - radius: 1.5 - color: d.hitColor - visible: d.dropIndex === rowIndex - Rectangle{ - width: 10 - height: 10 - radius: 5 - border.width: 3 - border.color: d.hitColor - color: FluTheme.dark ? FluColors.Black : FluColors.White - anchors{ - top: parent.top - left: parent.left - topMargin: -3 - leftMargin: -5 - } - } } FluRectangle{ width: 1 color: control.lineColor - visible: control.showLine && isItemLoader && itemModel.depth !== 0 && !itemModel.hasChildren() - height: itemModel.hideLineFooter() ? parent.height/2 : parent.height + visible: control.showLine && rowModel.depth !== 0 && !rowModel.hasChildren() + height: rowModel.hideLineFooter() ? parent.height/2 : parent.height anchors{ top: parent.top left: item_line_h.left @@ -291,7 +194,7 @@ Item { id:item_line_h height: 1 color: control.lineColor - visible: control.showLine && isItemLoader && itemModel.depth !== 0 && !itemModel.hasChildren() + visible: control.showLine && rowModel.depth !== 0 && !rowModel.hasChildren() width: depthPadding - 10 anchors{ right: layout_row.left @@ -300,12 +203,12 @@ Item { } } Repeater{ - model: Math.max(itemModel.depth-1,0) + model: Math.max(rowModel.depth-1,0) delegate: FluRectangle{ required property int index width: 1 color: control.lineColor - visible: control.showLine && isItemLoader && itemModel.depth !== 0 && itemModel.hasNextNodeByIndex(index) + visible: control.showLine && rowModel.depth !== 0 && rowModel.hasNextNodeByIndex(index) anchors{ top:parent.top bottom: parent.bottom @@ -314,57 +217,34 @@ Item { } } } - Rectangle{ - anchors.fill: parent - radius: 4 - anchors.leftMargin: 6 - anchors.rightMargin: 6 - border.color: d.hitColor - border.width: d.dragIndex === rowIndex ? 1 : 0 - color: { - if(isCurrent){ - return FluTheme.itemCheckColor - } - if(item_mouse.containsMouse || item_check_box.hovered){ - return FluTheme.itemHoverColor - } - if(item_loader_expand.item && item_loader_expand.item.hovered){ - return FluTheme.itemHoverColor - } - return FluTheme.itemNormalColor - } - } RowLayout{ id:layout_row height: parent.height - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left + anchors.fill: parent spacing: 0 - anchors.leftMargin: 14 + control.depthPadding*itemModel.depth + anchors.leftMargin: 14 + control.depthPadding*rowModel.depth Component{ id:com_icon_btn FluIconButton{ - opacity: itemModel.hasChildren() + opacity: rowModel.hasChildren() onClicked: { item_container.toggle() } contentItem:FluIcon{ - rotation: itemModel.isExpanded?0:-90 + rotation: rowModel.isExpanded?0:-90 iconSource:FluentIcons.ChevronDown iconSize: 16 anchors.centerIn: parent } } } - FluLoader{ id:item_loader_expand Layout.preferredWidth: 20 Layout.preferredHeight: 20 - sourceComponent: itemModel.hasChildren() ? com_icon_btn : undefined + sourceComponent: rowModel.hasChildren() ? com_icon_btn : undefined Layout.alignment: Qt.AlignVCenter } - FluCheckBox{ id:item_check_box Layout.preferredWidth: 18 @@ -372,39 +252,453 @@ Item { Layout.leftMargin: 5 horizontalPadding:0 verticalPadding: 0 - checked: itemModel.checked + checked: rowModel.checked animationEnabled:false visible: control.checkable padding: 0 clickListener: function(){ - tree_model.checkRow(rowIndex,!itemModel.checked) + tree_model.checkRow(row,!rowModel.checked) } Layout.alignment: Qt.AlignVCenter } - FluLoader{ - property var dataModel: itemModel - property var itemMouse: item_mouse - id:item_loader_cell - Layout.leftMargin: 10 - Layout.preferredWidth: { - if(item){ - return item.width - } - return 0 - } + Item{ Layout.fillHeight: true - sourceComponent:com_item_text + Layout.fillWidth: true + Layout.leftMargin: 6 + FluText { + id:item_text + text: String(display) + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + anchors.fill: parent + MouseArea{ + acceptedButtons: Qt.NoButton + id: hover_handler + hoverEnabled: true + anchors.fill: parent + } + FluTooltip{ + text: item_text.text + delay: 500 + visible: item_text.contentWidth < item_text.implicitWidth && hover_handler.containsMouse + } + } } + } } } + Component{ + id:com_other + FluText { + id:item_text + text: String(display) + elide: Text.ElideRight + wrapMode: Text.WrapAnywhere + anchors{ + fill: parent + leftMargin: 11 + rightMargin: 11 + topMargin: 6 + bottomMargin: 6 + } + verticalAlignment: Text.AlignVCenter + MouseArea{ + acceptedButtons: Qt.NoButton + id: hover_handler + hoverEnabled: true + anchors.fill: parent + } + FluTooltip{ + text: item_text.text + delay: 500 + visible: item_text.contentWidth < item_text.implicitWidth && item_text.contentHeight < item_text.implicitHeight && hover_handler.containsMouse + } + } + } + TableView{ + id:table_view + ScrollBar.horizontal: FluScrollBar{} + ScrollBar.vertical: FluScrollBar{} + boundsBehavior: Flickable.StopAtBounds + model: tree_model + anchors{ + top: header_horizontal.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + } + clip: true + columnWidthProvider: function(column) { + var columnObject = control.columnSource[column] + var width = columnObject.width + if(width){ + return width + } + var minimumWidth = columnObject.minimumWidth + if(minimumWidth){ + return minimumWidth + } + return d.defaultItemWidth + } + rowHeightProvider: function(row) { + return control.cellHeight + } + delegate: MouseArea{ + property var rowObject : rowModel.data + property alias isRowSelected: item_table_loader.isRowSelected + property var display: rowModel.data[columnModel.dataIndex] + property bool isObject: typeof(item_table.display) == "object" + property bool editVisible: { + if(rowObject && d.editPosition && d.editPosition._key === rowObject._key && d.editPosition.column === column){ + return true + } + return false + } + implicitHeight: 30 + implicitWidth: 30 + id: item_table + hoverEnabled: true + onEntered: { + d.rowHoverIndex = row + } + onWidthChanged: { + if(editVisible){ + updateEditPosition() + } + } + onHeightChanged: { + if(editVisible){ + updateEditPosition() + } + } + onXChanged: { + if(editVisible){ + updateEditPosition() + } + } + onYChanged: { + if(editVisible){ + updateEditPosition() + } + } + function updateEditPosition(){ + var obj = {} + obj._key = rowObject._key + obj.column = column + obj.row = row + obj.x = item_table.x + obj.y = item_table.y + 1 + obj.width = item_table.width + obj.height = item_table.height - 2 + d.editPosition = obj + } + onDoubleClicked:{ + if(typeof(display) == "object"){ + return + } + d.editDelegate = d.getEditDelegate(column) + updateEditPosition() + loader_edit.display = display + } + onClicked: + (event)=>{ + d.current = rowModel + event.accepted = true + } + Rectangle{ + anchors.fill: parent + color:{ + if(item_table.isRowSelected){ + return control.selectedColor + } + if(d.rowHoverIndex === row || item_table.isRowSelected){ + return FluTheme.dark ? Qt.rgba(1,1,1,0.06) : Qt.rgba(0,0,0,0.06) + } + return (row%2!==0) ? control.color : (FluTheme.dark ? Qt.rgba(1,1,1,0.015) : Qt.rgba(0,0,0,0.015)) + } + Item{ + anchors.fill: parent + visible: item_table.isRowSelected + Rectangle{ + width: 1 + height: parent.height + anchors.left: parent.left + color: control.selectedBorderColor + visible: column === 0 + } + Rectangle{ + width: 1 + height: parent.height + anchors.right: parent.right + color: control.selectedBorderColor + visible: column === control.columnSource.length-1 + } + Rectangle{ + width: parent.width + height: 1 + anchors.top: parent.top + color: control.selectedBorderColor + } + Rectangle{ + width: parent.width + height: 1 + anchors.bottom: parent.bottom + color: control.selectedBorderColor + } + } + } + FluLoader{ + anchors.fill: parent + id:item_table_loader + property var rowModel : model.rowModel + property var columnModel : model.columnModel + property int row : model.row + property int column: model.column + property var display: item_table.display + property bool isRowSelected: d.current === rowModel + property var options: { + if(isObject){ + return display.options + } + return {} + } + sourceComponent: { + if(column === 0) + return com_column + if(item_table.isObject){ + return item_table.display.comId + } + return com_other + } + } + } + FluLoader{ + id:loader_edit + property var tableView: control + property var display + property int column: { + if(d.editPosition){ + return d.editPosition.column + } + return 0 + } + property int row: { + if(d.editPosition){ + return d.editPosition.row + } + return 0 + } + signal editTextChaged(string text) + sourceComponent: d.editPosition ? d.editDelegate : undefined + onEditTextChaged: + (text)=>{ + const obj = tree_model.getRow(row).data + obj[control.columnSource[column].dataIndex] = text + tree_model.setRow(row,obj) + } + width: { + if(d.editPosition){ + return d.editPosition.width + } + return 0 + } + height: { + if(d.editPosition){ + return d.editPosition.height + } + return 0 + } + x:{ + if(d.editPosition){ + return d.editPosition.x + } + return 0 + } + y:{ + if(d.editPosition){ + return d.editPosition.y + } + return 0 + } + z:999 + } + } + TableModel{ + id:header_column_model + TableModelColumn {} + } + Component{ + id:com_column_text + FluText { + id: column_text + text: modelData + anchors.fill: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + Component{ + id:com_column_header_delegate + Rectangle{ + id:column_item_control + readonly property real cellPadding: 8 + property bool canceled: false + property int columnIndex: column + property var columnObject : control.columnSource[column] + implicitWidth: { + return (item_column_loader.item && item_column_loader.item.implicitWidth) + (cellPadding * 2) + } + implicitHeight: Math.max(36, (item_column_loader.item&&item_column_loader.item.implicitHeight) + (cellPadding * 2)) + color: FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(247/255,247/255,247/255,1) + Rectangle{ + border.color: control.borderColor + width: parent.width + height: 1 + anchors.top: parent.top + color:"#00000000" + } + Rectangle{ + border.color: control.borderColor + width: parent.width + height: 1 + anchors.bottom: parent.bottom + color:"#00000000" + } + Rectangle{ + border.color: control.borderColor + width: 1 + height: parent.height + anchors.left: parent.left + color:"#00000000" + } + Rectangle{ + border.color: control.borderColor + width: 1 + height: parent.height + anchors.right: parent.right + color:"#00000000" + visible: column === table_view.columns - 1 + } + MouseArea{ + id:column_item_control_mouse + anchors.fill: parent + anchors.rightMargin: 6 + hoverEnabled: true + onCanceled: { + column_item_control.canceled = true + } + onContainsMouseChanged: { + if(!containsMouse){ + column_item_control.canceled = false + } + } + onClicked: + (event)=>{ + + } + } + FluLoader{ + id:item_column_loader + property var itemModel: model + property var modelData: model.display + property var tableView: table_view + property var options:{ + if(typeof(modelData) == "object"){ + return modelData.options + } + return {} + } + property int column: column_item_control.columnIndex + width: parent.width + height: parent.height + sourceComponent: { + if(typeof(modelData) == "object"){ + return modelData.comId + } + return com_column_text + } + } + MouseArea{ + property point clickPos: "0,0" + height: parent.height + width: 6 + anchors.right: parent.right + acceptedButtons: Qt.LeftButton + hoverEnabled: true + visible: !(columnObject.width === columnObject.minimumWidth && columnObject.width === columnObject.maximumWidth && columnObject.width) + cursorShape: Qt.SplitHCursor + preventStealing: true + onPressed : + (mouse)=>{ + FluTools.setOverrideCursor(Qt.SplitHCursor) + clickPos = Qt.point(mouse.x, mouse.y) + } + onReleased:{ + FluTools.restoreOverrideCursor() + } + onCanceled: { + FluTools.restoreOverrideCursor() + } + onPositionChanged: + (mouse)=>{ + if(!pressed){ + return + } + var delta = Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y) + var minimumWidth = columnObject.minimumWidth + var maximumWidth = columnObject.maximumWidth + var w = columnObject.width + if(!w){ + w = d.defaultItemWidth + } + if(!minimumWidth){ + minimumWidth = d.defaultItemWidth + } + if(!maximumWidth){ + maximumWidth = 65535 + } + columnObject.width = Math.min(Math.max(minimumWidth, w + delta.x),maximumWidth) + table_view.forceLayout() + } + } + } + } + + TableView { + id: header_horizontal + model: header_column_model + anchors{ + left: table_view.left + right: table_view.right + top: parent.top + } + height: Math.max(1, contentHeight) + boundsBehavior: Flickable.StopAtBounds + clip: true + syncDirection: Qt.Horizontal + columnWidthProvider: table_view.columnWidthProvider + syncView: table_view.rows === 0 ? null : table_view + onContentXChanged:{ + timer_horizontal_force_layout.restart() + } + Timer{ + id:timer_horizontal_force_layout + interval: 50 + onTriggered: { + header_horizontal.forceLayout() + } + } + delegate: com_column_header_delegate + } + Component{ id:com_item_text Item{ width: item_text.width FluText { id:item_text - text: dataModel.title + text: model.title rightPadding: 14 anchors.centerIn: parent color:{ @@ -423,7 +717,7 @@ Item { return tree_model.dataSourceSize } function visibleCount(){ - return table_view.count + return table_view.rows } function collapse(rowIndex){ tree_model.collapse(rowIndex) @@ -437,4 +731,14 @@ Item { function allCollapse(){ tree_model.allCollapse() } + function customItem(comId,options={}){ + var o = {} + o.comId = comId + o.options = options + return o + } + function closeEditor(){ + d.editPosition = undefined + d.editDelegate = undefined + } }