From d93aac3518f9c6f6a6ddb376e7435e2eeff0f73a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Sun, 28 Apr 2024 15:56:37 +0800 Subject: [PATCH 01/35] update --- example/example_en_US.ts | 22 ++++----- example/example_zh_CN.ts | 4 +- example/qml/page/T_TableView.qml | 16 +++---- src/FluFrameless.cpp | 45 +++++++++++++------ src/FluTableSortProxyModel.cpp | 4 ++ src/FluTableSortProxyModel.h | 2 + .../FluentUI/Controls/FluTableView.qml | 19 ++++++++ .../imports/FluentUI/Controls/FluWindow.qml | 2 +- .../FluentUI/Controls/FluTableView.qml | 19 ++++++++ .../imports/FluentUI/Controls/FluWindow.qml | 2 +- 10 files changed, 98 insertions(+), 37 deletions(-) diff --git a/example/example_en_US.ts b/example/example_en_US.ts index ceabe270..e88eb62c 100644 --- a/example/example_en_US.ts +++ b/example/example_en_US.ts @@ -1998,7 +1998,17 @@ Some contents... - + + Delete Selection + + + + + Add a row of Data + + + + Insert a Row @@ -2042,16 +2052,6 @@ Some contents... Next> - - - Delete Selection - - - - - Add a row of Data - - T_Text diff --git a/example/example_zh_CN.ts b/example/example_zh_CN.ts index c86300be..f6d98a5e 100644 --- a/example/example_zh_CN.ts +++ b/example/example_zh_CN.ts @@ -2140,7 +2140,7 @@ Some contents... 清除所有 - + Insert a Row 插入一行 @@ -2190,7 +2190,7 @@ Some contents... 删除选中 - + Add a row of Data 添加一行数据 diff --git a/example/qml/page/T_TableView.qml b/example/qml/page/T_TableView.qml index b309c369..8dee3a97 100644 --- a/example/qml/page/T_TableView.qml +++ b/example/qml/page/T_TableView.qml @@ -442,15 +442,15 @@ FluContentPage{ var data = [] var rows = [] for (var i = 0; i < table_view.rows; i++) { - var item = table_view.getRow(i); + var item = table_view.getRow(i) rows.push(item) if (!item.checkbox.options.checked) { data.push(item); } } - var sourceModel = table_view.sourceModel; + var sourceModel = table_view.sourceModel for (i = 0; i < sourceModel.rowCount; i++) { - var sourceItem = sourceModel.getRow(i); + var sourceItem = sourceModel.getRow(i) const foundItem = rows.find(item=> item._key === sourceItem._key) if (!foundItem) { data.push(sourceItem); @@ -459,7 +459,6 @@ FluContentPage{ table_view.dataSource = data } } - FluButton{ text: qsTr("Add a row of Data") onClicked: { @@ -470,10 +469,11 @@ FluContentPage{ text: qsTr("Insert a Row") onClicked: { if(typeof table_view.current !== 'undefined'){ - var newLine = genTestObject() - var currentLine = dataSource.findIndex(obj => obj._key === table_view.current._key) - root.dataSource.splice(currentLine, 0, newLine); - table_view.dataSource = root.dataSource + var index = table_view.currentIndex() + if(index !== -1){ + var testObj = genTestObject() + table_view.insertRow(index,testObj) + } }else{ showWarning(qsTr("Focus not acquired: Please click any item in the form as the target for insertion!")) } diff --git a/src/FluFrameless.cpp b/src/FluFrameless.cpp index 6f92e2c7..0c2a2337 100644 --- a/src/FluFrameless.cpp +++ b/src/FluFrameless.cpp @@ -35,6 +35,22 @@ static inline bool isCompositionEnabled() { return false; } +static inline void setShadow(HWND hwnd){ + const MARGINS shadow = { 1, 1, 1, 1 }; + typedef HRESULT (WINAPI* DwmExtendFrameIntoClientAreaPtr)(HWND hWnd, const MARGINS *pMarInset); + HMODULE module = LoadLibraryW(L"dwmapi.dll"); + if (module) + { + DwmExtendFrameIntoClientAreaPtr dwm_extendframe_into_client_area_; + dwm_extendframe_into_client_area_= reinterpret_cast(GetProcAddress(module, "DwmExtendFrameIntoClientArea")); + if (dwm_extendframe_into_client_area_) + { + dwm_extendframe_into_client_area_(hwnd, &shadow); + } + } +} + + #endif bool containsCursorToItem(QQuickItem *item) { @@ -106,6 +122,7 @@ void FluFrameless::componentComplete() { ::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOOWNERZORDER); ::RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW); }); + setShadow(hwnd); #endif h = qRound(h + _appbar->height()); if (_fixSize) { @@ -147,20 +164,22 @@ void FluFrameless::componentComplete() { } else if (uMsg == WM_NCCALCSIZE) { const auto clientRect = ((wParam == FALSE) ? reinterpret_cast(lParam) : &(reinterpret_cast(lParam))->rgrc[0]); bool isMaximum = ::IsZoomed(hwnd); + const LONG originalTop = clientRect->top; + const LONG originalLeft = clientRect->left; + const LONG originalBottom = clientRect->bottom; + const LONG originalRight = clientRect->right; + const LRESULT hitTestResult = ::DefWindowProcW(hwnd, WM_NCCALCSIZE, wParam, lParam); + if ((hitTestResult != HTERROR) && (hitTestResult != HTNOWHERE)) { + *result = static_cast(hitTestResult); + return true; + } if (!isMaximum){ - if (clientRect->top != 0) - { - clientRect->top -= 1; - clientRect->bottom -= 1; - } + clientRect->top = originalTop; + clientRect->bottom = originalBottom; + clientRect->left = originalLeft; + clientRect->right = originalRight; } else{ - const LONG originalTop = clientRect->top; - const LRESULT hitTestResult = ::DefWindowProcW(hwnd, WM_NCCALCSIZE, wParam, lParam); - if ((hitTestResult != HTERROR) && (hitTestResult != HTNOWHERE)) { - *result = static_cast(hitTestResult); - return true; - } - clientRect->top = originalTop-originalTop; + clientRect->top = 0; } _setMaximizeHovered(false); *result = WVR_REDRAW; @@ -244,8 +263,6 @@ void FluFrameless::componentComplete() { _setMaximizePressed(false); return true; } - } else if (uMsg == WM_ERASEBKGND) { - return true; } else if (uMsg == WM_NCRBUTTONDOWN) { if (wParam == HTCAPTION) { _showSystemMenu(QCursor::pos()); diff --git a/src/FluTableSortProxyModel.cpp b/src/FluTableSortProxyModel.cpp index 644f652a..1723a678 100644 --- a/src/FluTableSortProxyModel.cpp +++ b/src/FluTableSortProxyModel.cpp @@ -67,6 +67,10 @@ bool FluTableSortProxyModel::lessThan(const QModelIndex &source_left, const QMod QMetaObject::invokeMethod(_model, "setRow", Q_ARG(int, mapToSource(index(rowIndex, 0)).row()), Q_ARG(QVariant, val)); } +[[maybe_unused]] void FluTableSortProxyModel::insertRow(int rowIndex, const QVariant &val) { + QMetaObject::invokeMethod(_model, "insertRow", Q_ARG(int, mapToSource(index(rowIndex, 0)).row()), Q_ARG(QVariant, val)); +} + [[maybe_unused]] void FluTableSortProxyModel::removeRow(int rowIndex, int rows) { QMetaObject::invokeMethod(_model, "removeRow", Q_ARG(int, mapToSource(index(rowIndex, 0)).row()), Q_ARG(int, rows)); } diff --git a/src/FluTableSortProxyModel.h b/src/FluTableSortProxyModel.h index 7a959f41..05fb1d9e 100644 --- a/src/FluTableSortProxyModel.h +++ b/src/FluTableSortProxyModel.h @@ -23,6 +23,8 @@ public: [[maybe_unused]] Q_INVOKABLE void setRow(int rowIndex, const QVariant &val); + [[maybe_unused]] Q_INVOKABLE void insertRow(int rowIndex, const QVariant &val); + [[maybe_unused]] Q_INVOKABLE void removeRow(int rowIndex, int rows); [[maybe_unused]] Q_INVOKABLE void setComparator(const QJSValue &comparator); diff --git a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml index b09cab1b..31f81191 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml @@ -873,6 +873,25 @@ Rectangle { table_view.model.removeRow(rowIndex,rows) } } + function insertRow(rowIndex,obj){ + if(rowIndex>=0 && rowIndex=0 && rowIndex Date: Sun, 28 Apr 2024 18:19:21 +0800 Subject: [PATCH 02/35] update --- src/FluFrameless.cpp | 6 +- src/FluTreeModel.cpp | 5 +- src/FluTreeModel.h | 3 +- src/FluentUI.cpp | 2 + .../FluentUI/Controls/FluTableView.qml | 71 +++++++++---------- .../imports/FluentUI/Controls/FluWindow.qml | 1 + .../FluentUI/Controls/FluTableView.qml | 71 +++++++++---------- .../imports/FluentUI/Controls/FluWindow.qml | 1 + 8 files changed, 75 insertions(+), 85 deletions(-) diff --git a/src/FluFrameless.cpp b/src/FluFrameless.cpp index 0c2a2337..ec423f8a 100644 --- a/src/FluFrameless.cpp +++ b/src/FluFrameless.cpp @@ -36,7 +36,7 @@ static inline bool isCompositionEnabled() { } static inline void setShadow(HWND hwnd){ - const MARGINS shadow = { 1, 1, 1, 1 }; + const MARGINS shadow = { 1, 0, 0, 0 }; typedef HRESULT (WINAPI* DwmExtendFrameIntoClientAreaPtr)(HWND hWnd, const MARGINS *pMarInset); HMODULE module = LoadLibraryW(L"dwmapi.dll"); if (module) @@ -122,7 +122,9 @@ void FluFrameless::componentComplete() { ::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOOWNERZORDER); ::RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW); }); - setShadow(hwnd); + if(!window()->property("_hideShadow").toBool()){ + setShadow(hwnd); + } #endif h = qRound(h + _appbar->height()); if (_fixSize) { diff --git a/src/FluTreeModel.cpp b/src/FluTreeModel.cpp index 020e8af8..44b3ae2e 100644 --- a/src/FluTreeModel.cpp +++ b/src/FluTreeModel.cpp @@ -1,12 +1,11 @@ #include "FluTreeModel.h" #include -#include FluTreeNode::FluTreeNode(QObject *parent) : QObject{parent} { } -FluTreeModel::FluTreeModel(QObject *parent) : QAbstractItemModel{parent} { +FluTreeModel::FluTreeModel(QObject *parent) : QAbstractTableModel{parent} { _dataSourceSize = 0; } @@ -17,7 +16,7 @@ QModelIndex FluTreeModel::parent(const QModelIndex &child) const { QModelIndex FluTreeModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent) || parent.isValid()) return {}; - return createIndex(row, column, _rows.at(row)); + return createIndex(row, column); } int FluTreeModel::rowCount(const QModelIndex &parent) const { diff --git a/src/FluTreeModel.h b/src/FluTreeModel.h index d8eda4f5..405e766c 100644 --- a/src/FluTreeModel.h +++ b/src/FluTreeModel.h @@ -86,12 +86,11 @@ public: FluTreeNode *_parent = nullptr; }; -class FluTreeModel : public QAbstractItemModel { +class FluTreeModel : public QAbstractTableModel { Q_OBJECT Q_PROPERTY_AUTO(int, dataSourceSize) Q_PROPERTY_AUTO(QList, columnSource) QML_NAMED_ELEMENT(FluTreeModel) - QML_ADDED_IN_MINOR_VERSION(1) public: enum TreeModelRoles { RowModel = 0x0101, diff --git a/src/FluentUI.cpp b/src/FluentUI.cpp index a1704d39..bf86380c 100644 --- a/src/FluentUI.cpp +++ b/src/FluentUI.cpp @@ -15,6 +15,7 @@ #include "FluQrCodeItem.h" #include "FluTableSortProxyModel.h" #include "FluFrameless.h" +#include "FluTableModel.h" void FluentUI::registerTypes(QQmlEngine *engine) { initializeEngine(engine, _uri); @@ -32,6 +33,7 @@ void FluentUI::registerTypes(const char *uri) const { qmlRegisterType(uri, major, minor, "FluWatermark"); qmlRegisterType(uri, major, minor, "FluAccentColor"); qmlRegisterType(uri, major, minor, "FluTreeModel"); + qmlRegisterType(uri, major, minor, "FluTableModel"); qmlRegisterType(uri, major, minor, "FluRectangle"); qmlRegisterType(uri, major, minor, "FluFrameless"); qmlRegisterType(uri, major, minor, "FluTableSortProxyModel"); diff --git a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml index 31f81191..099e8690 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml @@ -10,7 +10,7 @@ Rectangle { readonly property alias columns: table_view.columns readonly property alias current: d.current readonly property alias sourceModel: table_model - property var columnSource + property var columnSource: [] property var dataSource property color borderColor: FluTheme.dark ? Qt.rgba(37/255,37/255,37/255,1) : Qt.rgba(228/255,228/255,228/255,1) property bool horizonalHeaderVisible: true @@ -27,17 +27,13 @@ Rectangle { onColumnSourceChanged: { if(columnSource.length!==0){ var columns= [] - var columnsData = [] var headerRow = {} columnSource.forEach(function(item){ var column = Qt.createQmlObject('import Qt.labs.qmlmodels 1.0;TableModelColumn{}',table_model); column.display = item.dataIndex - columnsData.push(item) columns.push(column) headerRow[item.dataIndex] = item.title }) - d.columns_data = columnsData - table_model.columns = columns header_column_model.columns = columns header_column_model.rows = [headerRow] } @@ -48,15 +44,14 @@ Rectangle { property int rowHoverIndex: -1 property int defaultItemWidth: 100 property int defaultItemHeight: 42 - property var columns_data: [] property var editDelegate property var editPosition function getEditDelegate(column){ - var obj =d.columns_data[column].editDelegate + var obj =control.columnSource[column].editDelegate if(obj){ return obj } - if(d.columns_data[column].editMultiline === true){ + if(control.columnSource[column].editMultiline === true){ return com_edit_multiline } return com_edit @@ -66,13 +61,13 @@ Rectangle { table_model.clear() table_model.rows = dataSource } - TableModel { + FluTableModel { id:table_model - TableModelColumn {} + columnSource: control.columnSource } TableModel{ id:header_column_model - TableModelColumn {} + TableModelColumn { display : "title"} } TableModel{ id:header_row_model @@ -87,7 +82,7 @@ Rectangle { FluTextBox{ id:text_box text: String(display) - readOnly: true === d.columns_data[column].readOnly + readOnly: true === control.columnSource[column].readOnly Component.onCompleted: { forceActiveFocus() selectAll() @@ -113,7 +108,7 @@ Rectangle { TextArea.flickable: FluMultilineTextBox { id:text_box text: String(display) - readOnly: true === d.columns_data[column].readOnly + readOnly: true === control.columnSource[column].readOnly verticalAlignment: TextInput.AlignVCenter isCtrlEnterForNewline: true Component.onCompleted: { @@ -196,15 +191,21 @@ Rectangle { id:com_table_delegate MouseArea{ id:item_table_mouse + property bool isRowSelected: { + if(rowModel === null) + return false + if(d.current){ + return rowModel._key === d.current._key + } + return false + } TableView.onPooled: { if(d.editPosition && d.editPosition.row === row && d.editPosition.column === column){ control.closeEditor() } } - property var rowObject : control.getRow(row) - property var itemModel: model property bool editVisible: { - if(rowObject && d.editPosition && d.editPosition._key === rowObject._key && d.editPosition.column === column){ + if(d.editPosition && d.editPosition._key === rowModel._key && d.editPosition.column === column){ return true } return false @@ -235,7 +236,7 @@ Rectangle { } function updateEditPosition(){ var obj = {} - obj._key = rowObject._key + obj._key = rowModel._key obj.column = column obj.row = row obj.x = item_table_mouse.x @@ -245,22 +246,12 @@ Rectangle { d.editPosition = obj } Rectangle{ - id:item_table anchors.fill: parent - property point position: Qt.point(column,row) - property bool isRowSelected: { - if(rowObject === null) - return false - if(d.current){ - return rowObject._key === d.current._key - } - return false - } color:{ - if(item_table.isRowSelected){ + if(item_table_mouse.isRowSelected){ return control.selectedColor } - if(d.rowHoverIndex === row || item_table.isRowSelected){ + if(d.rowHoverIndex === row || item_table_mouse.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)) @@ -279,22 +270,24 @@ Rectangle { if(typeof(display) == "object"){ return } - loader_edit.display = display + loader_edit.display = item_table_loader.display d.editDelegate = d.getEditDelegate(column) updateEditPosition() } onClicked: (event)=>{ - d.current = rowObject + d.current = rowModel control.closeEditor() event.accepted = true } } FluLoader{ - property var model: itemModel - property var display: itemModel.display - property int row: item_table.position.y - property int column: item_table.position.x + id: item_table_loader + property var display: rowModel[columnModel.dataIndex] + property var rowModel : model.rowModel + property var columnModel : model.columnModel + property int row : model.row + property int column: model.column property bool isObject: typeof(display) == "object" property var options: { if(isObject){ @@ -312,7 +305,7 @@ Rectangle { } Item{ anchors.fill: parent - visible: item_table.isRowSelected + visible: item_table_mouse.isRowSelected Rectangle{ width: 1 height: parent.height @@ -365,7 +358,7 @@ Rectangle { ScrollBar.horizontal:scroll_bar_h ScrollBar.vertical:scroll_bar_v columnWidthProvider: function(column) { - var columnObject = d.columns_data[column] + var columnObject = control.columnSource[column] var width = columnObject.width if(width){ return width @@ -416,7 +409,7 @@ Rectangle { onEditTextChaged: (text)=>{ var obj = control.getRow(row) - obj[d.columns_data[column].dataIndex] = text + obj[control.columnSource[column].dataIndex] = text control.setRow(row,obj) } width: { @@ -454,7 +447,7 @@ Rectangle { readonly property real cellPadding: 8 property bool canceled: false property int columnIndex: column - readonly property var columnObject : d.columns_data[column] + readonly property var columnObject : control.columnSource[column] implicitWidth: { return (item_column_loader.item && item_column_loader.item.implicitWidth) + (cellPadding * 2) } diff --git a/src/Qt5/imports/FluentUI/Controls/FluWindow.qml b/src/Qt5/imports/FluentUI/Controls/FluWindow.qml index c363a588..8600d29c 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluWindow.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluWindow.qml @@ -60,6 +60,7 @@ Window { signal lazyLoad() property var _windowRegister property string _route + property bool _hideShadow: false id:window color:"transparent" Component.onCompleted: { diff --git a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml index cc9a6714..9d7eca69 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml @@ -10,7 +10,7 @@ Rectangle { readonly property alias columns: table_view.columns readonly property alias current: d.current readonly property alias sourceModel: table_model - property var columnSource + property var columnSource: [] property var dataSource property color borderColor: FluTheme.dark ? Qt.rgba(37/255,37/255,37/255,1) : Qt.rgba(228/255,228/255,228/255,1) property bool horizonalHeaderVisible: true @@ -27,17 +27,13 @@ Rectangle { onColumnSourceChanged: { if(columnSource.length!==0){ var columns= [] - var columnsData = [] var headerRow = {} columnSource.forEach(function(item){ var column = Qt.createQmlObject('import Qt.labs.qmlmodels 1.0;TableModelColumn{}',table_model); column.display = item.dataIndex - columnsData.push(item) columns.push(column) headerRow[item.dataIndex] = item.title }) - d.columns_data = columnsData - table_model.columns = columns header_column_model.columns = columns header_column_model.rows = [headerRow] } @@ -48,15 +44,14 @@ Rectangle { property int rowHoverIndex: -1 property int defaultItemWidth: 100 property int defaultItemHeight: 42 - property var columns_data: [] property var editDelegate property var editPosition function getEditDelegate(column){ - var obj =d.columns_data[column].editDelegate + var obj =control.columnSource[column].editDelegate if(obj){ return obj } - if(d.columns_data[column].editMultiline === true){ + if(control.columnSource[column].editMultiline === true){ return com_edit_multiline } return com_edit @@ -66,13 +61,13 @@ Rectangle { table_model.clear() table_model.rows = dataSource } - TableModel { + FluTableModel { id:table_model - TableModelColumn {} + columnSource: control.columnSource } TableModel{ id:header_column_model - TableModelColumn {} + TableModelColumn { display : "title"} } TableModel{ id:header_row_model @@ -87,7 +82,7 @@ Rectangle { FluTextBox{ id:text_box text: String(display) - readOnly: true === d.columns_data[column].readOnly + readOnly: true === control.columnSource[column].readOnly Component.onCompleted: { forceActiveFocus() selectAll() @@ -113,7 +108,7 @@ Rectangle { TextArea.flickable: FluMultilineTextBox { id:text_box text: String(display) - readOnly: true === d.columns_data[column].readOnly + readOnly: true === control.columnSource[column].readOnly verticalAlignment: TextInput.AlignVCenter isCtrlEnterForNewline: true Component.onCompleted: { @@ -196,15 +191,21 @@ Rectangle { id:com_table_delegate MouseArea{ id:item_table_mouse + property bool isRowSelected: { + if(rowModel === null) + return false + if(d.current){ + return rowModel._key === d.current._key + } + return false + } TableView.onPooled: { if(d.editPosition && d.editPosition.row === row && d.editPosition.column === column){ control.closeEditor() } } - property var rowObject : control.getRow(row) - property var itemModel: model property bool editVisible: { - if(rowObject && d.editPosition && d.editPosition._key === rowObject._key && d.editPosition.column === column){ + if(d.editPosition && d.editPosition._key === rowModel._key && d.editPosition.column === column){ return true } return false @@ -235,7 +236,7 @@ Rectangle { } function updateEditPosition(){ var obj = {} - obj._key = rowObject._key + obj._key = rowModel._key obj.column = column obj.row = row obj.x = item_table_mouse.x @@ -245,22 +246,12 @@ Rectangle { d.editPosition = obj } Rectangle{ - id:item_table anchors.fill: parent - property point position: Qt.point(column,row) - property bool isRowSelected: { - if(rowObject === null) - return false - if(d.current){ - return rowObject._key === d.current._key - } - return false - } color:{ - if(item_table.isRowSelected){ + if(item_table_mouse.isRowSelected){ return control.selectedColor } - if(d.rowHoverIndex === row || item_table.isRowSelected){ + if(d.rowHoverIndex === row || item_table_mouse.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)) @@ -279,22 +270,24 @@ Rectangle { if(typeof(display) == "object"){ return } - loader_edit.display = display + loader_edit.display = item_table_loader.display d.editDelegate = d.getEditDelegate(column) updateEditPosition() } onClicked: (event)=>{ - d.current = rowObject + d.current = rowModel control.closeEditor() event.accepted = true } } FluLoader{ - property var model: itemModel - property var display: itemModel.display - property int row: item_table.position.y - property int column: item_table.position.x + id: item_table_loader + property var display: rowModel[columnModel.dataIndex] + property var rowModel : model.rowModel + property var columnModel : model.columnModel + property int row : model.row + property int column: model.column property bool isObject: typeof(display) == "object" property var options: { if(isObject){ @@ -312,7 +305,7 @@ Rectangle { } Item{ anchors.fill: parent - visible: item_table.isRowSelected + visible: item_table_mouse.isRowSelected Rectangle{ width: 1 height: parent.height @@ -365,7 +358,7 @@ Rectangle { ScrollBar.horizontal:scroll_bar_h ScrollBar.vertical:scroll_bar_v columnWidthProvider: function(column) { - var columnObject = d.columns_data[column] + var columnObject = control.columnSource[column] var width = columnObject.width if(width){ return width @@ -416,7 +409,7 @@ Rectangle { onEditTextChaged: (text)=>{ var obj = control.getRow(row) - obj[d.columns_data[column].dataIndex] = text + obj[control.columnSource[column].dataIndex] = text control.setRow(row,obj) } width: { @@ -454,7 +447,7 @@ Rectangle { readonly property real cellPadding: 8 property bool canceled: false property int columnIndex: column - readonly property var columnObject : d.columns_data[column] + readonly property var columnObject : control.columnSource[column] implicitWidth: { return (item_column_loader.item && item_column_loader.item.implicitWidth) + (cellPadding * 2) } diff --git a/src/Qt6/imports/FluentUI/Controls/FluWindow.qml b/src/Qt6/imports/FluentUI/Controls/FluWindow.qml index da7b7e9b..851b879e 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluWindow.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluWindow.qml @@ -59,6 +59,7 @@ Window { signal lazyLoad() property var _windowRegister property string _route + property bool _hideShadow: false id:window color:"transparent" Component.onCompleted: { From c733f3c60e5ad61b3acd067a3e628bd3762d6b8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Sun, 28 Apr 2024 18:38:37 +0800 Subject: [PATCH 03/35] update --- example/example_en_US.ts | 18 ++--- example/example_zh_CN.ts | 18 ++--- example/qml/page/T_TableView.qml | 12 +-- src/FluTableModel.cpp | 73 +++++++++++++++++++ src/FluTableModel.h | 51 +++++++++++++ .../FluentUI/Controls/FluTableView.qml | 2 +- .../FluentUI/Controls/FluTableView.qml | 2 +- 7 files changed, 148 insertions(+), 28 deletions(-) create mode 100644 src/FluTableModel.cpp create mode 100644 src/FluTableModel.h diff --git a/example/example_en_US.ts b/example/example_en_US.ts index e88eb62c..36ff648b 100644 --- a/example/example_en_US.ts +++ b/example/example_en_US.ts @@ -1968,7 +1968,7 @@ Some contents... - + Name @@ -2013,42 +2013,42 @@ Some contents... - + Focus not acquired: Please click any item in the form as the target for insertion! - + Avatar - + Address - + Nickname - + Long String - + Options - + <Previous - + Next> diff --git a/example/example_zh_CN.ts b/example/example_zh_CN.ts index f6d98a5e..8d1b2571 100644 --- a/example/example_zh_CN.ts +++ b/example/example_zh_CN.ts @@ -2110,7 +2110,7 @@ Some contents... - + Name 名称 @@ -2145,42 +2145,42 @@ Some contents... 插入一行 - + Focus not acquired: Please click any item in the form as the target for insertion! 焦点未获取:请点击表格中的任意一项,作为插入的靶点! - + Avatar 头像 - + Address 地址 - + Nickname 昵称 - + Long String 长字符串 - + Options 操作 - + <Previous <上一页 - + Next> 下一页> diff --git a/example/qml/page/T_TableView.qml b/example/qml/page/T_TableView.qml index 8dee3a97..94a7aad0 100644 --- a/example/qml/page/T_TableView.qml +++ b/example/qml/page/T_TableView.qml @@ -468,19 +468,15 @@ FluContentPage{ FluButton{ text: qsTr("Insert a Row") onClicked: { - if(typeof table_view.current !== 'undefined'){ - var index = table_view.currentIndex() - if(index !== -1){ - var testObj = genTestObject() - table_view.insertRow(index,testObj) - } + var index = table_view.currentIndex() + if(index !== -1){ + var testObj = genTestObject() + table_view.insertRow(index,testObj) }else{ showWarning(qsTr("Focus not acquired: Please click any item in the form as the target for insertion!")) } - } } - } } diff --git a/src/FluTableModel.cpp b/src/FluTableModel.cpp new file mode 100644 index 00000000..ac74bd67 --- /dev/null +++ b/src/FluTableModel.cpp @@ -0,0 +1,73 @@ +#include "FluTableModel.h" + +FluTableModel::FluTableModel(QObject *parent) : QAbstractTableModel{parent}{ + +} + +int FluTableModel::rowCount(const QModelIndex &parent) const { + return _rows.count(); +} + +int FluTableModel::columnCount(const QModelIndex &parent) const { + return this->_columnSource.size(); +} + +QVariant FluTableModel::data(const QModelIndex &index, int role) const { + switch (role) { + case FluTableModel::RowModel: + return QVariant::fromValue(_rows.at(index.row())); + case FluTableModel::ColumnModel: + return QVariant::fromValue(_columnSource.at(index.column())); + default: + break; + } + return {}; +} + +QHash FluTableModel::roleNames() const { + return { + {FluTableModel::RowModel, "rowModel"}, + {FluTableModel::ColumnModel, "columnModel"} + }; +} + +QModelIndex FluTableModel::parent(const QModelIndex &child) const { + return {}; +} + +QModelIndex FluTableModel::index(int row, int column, const QModelIndex &parent) const { + if (!hasIndex(row, column, parent) || parent.isValid()) + return {}; + return createIndex(row, column); +} + +void FluTableModel::clear(){ + beginResetModel(); + this->_rows.clear(); + endResetModel(); +} + +QVariant FluTableModel::getRow(int rowIndex){ + return _rows.at(rowIndex); +} + +void FluTableModel::setRow(int rowIndex,QVariant row){ + _rows.replace(rowIndex,row.toMap()); + Q_EMIT dataChanged(index(rowIndex, 0), index(rowIndex, columnCount() - 1)); +} + +void FluTableModel::insertRow(int rowIndex,QVariant row){ + beginInsertRows(QModelIndex(), rowIndex, rowIndex); + _rows.insert(rowIndex,row.toMap()); + endInsertRows(); +} + +void FluTableModel::removeRow(int rowIndex,int rows){ + beginRemoveRows(QModelIndex(), rowIndex, rowIndex + rows - 1); + _rows = _rows.mid(0, rowIndex) + _rows.mid(rowIndex + rows); + endRemoveRows(); +} + +void FluTableModel::appendRow(QVariant row){ + insertRow(rowCount(),row); +} diff --git a/src/FluTableModel.h b/src/FluTableModel.h new file mode 100644 index 00000000..15b72435 --- /dev/null +++ b/src/FluTableModel.h @@ -0,0 +1,51 @@ +#ifndef FLUTABLEMODEL_H +#define FLUTABLEMODEL_H + +#include +#include +#include +#include "stdafx.h" + +class FluTableModel : public QAbstractTableModel { + Q_OBJECT + Q_PROPERTY_AUTO(int, dataSourceSize) + Q_PROPERTY_AUTO(QList, columnSource) + Q_PROPERTY_AUTO(QList, rows) + Q_PROPERTY(int rowCount READ rowCount CONSTANT) + QML_NAMED_ELEMENT(FluTableModel) +public: + enum TableModelRoles { + RowModel = 0x0101, + ColumnModel = 0x0102 + }; + + explicit FluTableModel(QObject *parent = nullptr); + + [[nodiscard]] int rowCount(const QModelIndex &parent = {}) const override; + + [[nodiscard]] int columnCount(const QModelIndex &parent = {}) const override; + + [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + [[nodiscard]] QHash roleNames() const override; + + [[nodiscard]] QModelIndex parent(const QModelIndex &child) const override; + + [[nodiscard]] QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override; + + Q_INVOKABLE void clear(); + + Q_INVOKABLE QVariant getRow(int rowIndex); + + Q_INVOKABLE void setRow(int rowIndex,QVariant row); + + Q_INVOKABLE void insertRow(int rowIndex,QVariant row); + + Q_INVOKABLE void removeRow(int rowIndex,int rows = 1); + + Q_INVOKABLE void appendRow(QVariant row); + +}; + + +#endif // FLUTABLEMODEL_H diff --git a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml index 099e8690..029c4e10 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml @@ -267,7 +267,7 @@ Rectangle { onReleased: { } onDoubleClicked:{ - if(typeof(display) == "object"){ + if(item_table_loader.isObject){ return } loader_edit.display = item_table_loader.display diff --git a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml index 9d7eca69..f41bd9b4 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml @@ -267,7 +267,7 @@ Rectangle { onReleased: { } onDoubleClicked:{ - if(typeof(display) == "object"){ + if(item_table_loader.isObject){ return } loader_edit.display = item_table_loader.display From 96a6d0e7fa085aa77e94b591e5a3bcfe48e934e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Sun, 28 Apr 2024 20:22:05 +0800 Subject: [PATCH 04/35] update --- example/example_en_US.ts | 28 ++++---- example/example_zh_CN.ts | 28 ++++---- example/qml/page/T_TableView.qml | 3 +- src/FluApp.cpp | 3 +- src/FluApp.h | 2 +- src/FluFrameless.cpp | 70 +++++++++---------- src/FluTableModel.cpp | 36 +++++----- src/FluTableModel.h | 14 ++-- src/FluTableSortProxyModel.cpp | 12 ++-- src/FluTableSortProxyModel.h | 2 +- src/FluTools.h | 2 +- src/FluTreeModel.cpp | 10 +-- src/FluTreeModel.h | 2 +- .../imports/FluentUI/Controls/FluShadow.qml | 2 +- .../FluentUI/Controls/FluTableView.qml | 51 +++++++------- .../imports/FluentUI/Controls/FluShadow.qml | 2 +- .../FluentUI/Controls/FluTableView.qml | 51 +++++++------- 17 files changed, 158 insertions(+), 160 deletions(-) diff --git a/example/example_en_US.ts b/example/example_en_US.ts index 36ff648b..4ac68c4d 100644 --- a/example/example_en_US.ts +++ b/example/example_en_US.ts @@ -1968,7 +1968,7 @@ Some contents... - + Name @@ -1988,67 +1988,67 @@ Some contents... - + Age - + Clear All - + Delete Selection - + Add a row of Data - + Insert a Row - + Focus not acquired: Please click any item in the form as the target for insertion! - + Avatar - + Address - + Nickname - + Long String - + Options - + <Previous - + Next> diff --git a/example/example_zh_CN.ts b/example/example_zh_CN.ts index 8d1b2571..5880157d 100644 --- a/example/example_zh_CN.ts +++ b/example/example_zh_CN.ts @@ -2110,7 +2110,7 @@ Some contents... - + Name 名称 @@ -2130,67 +2130,67 @@ Some contents... 全选 - + Age 年龄 - + Clear All 清除所有 - + Insert a Row 插入一行 - + Focus not acquired: Please click any item in the form as the target for insertion! 焦点未获取:请点击表格中的任意一项,作为插入的靶点! - + Avatar 头像 - + Address 地址 - + Nickname 昵称 - + Long String 长字符串 - + Options 操作 - + <Previous <上一页 - + Next> 下一页> - + Delete Selection 删除选中 - + Add a row of Data 添加一行数据 diff --git a/example/qml/page/T_TableView.qml b/example/qml/page/T_TableView.qml index 94a7aad0..fe0f6e93 100644 --- a/example/qml/page/T_TableView.qml +++ b/example/qml/page/T_TableView.qml @@ -244,7 +244,7 @@ FluContentPage{ clickListener: function(){ root.selectedAll = !root.selectedAll var checked = root.selectedAll - itemModel.display = table_view.customItem(com_column_checbox,{"checked":checked}) + model.display = table_view.customItem(com_column_checbox,{"checked":checked}) for(var i =0;i< table_view.rows ;i++){ var rowData = table_view.getRow(i) rowData.checkbox = table_view.customItem(com_checbox,{"checked":checked}) @@ -293,6 +293,7 @@ FluContentPage{ }); items = result textbox.text= String(display) + selectAll() } onCommit: { editTextChaged(textbox.text) diff --git a/src/FluApp.cpp b/src/FluApp.cpp index 36bfd3fa..82226782 100644 --- a/src/FluApp.cpp +++ b/src/FluApp.cpp @@ -1,6 +1,5 @@ #include "FluApp.h" -#include #include #include #include @@ -32,7 +31,7 @@ void FluApp::init(QObject *target, QLocale locale) { } } -[[maybe_unused]] QJsonArray FluApp::iconDatas(const QString &keyword) { +[[maybe_unused]] QJsonArray FluApp::iconData(const QString &keyword) { QJsonArray arr; QMetaEnum enumType = Fluent_Icons::staticMetaObject.enumerator(Fluent_Icons::staticMetaObject.indexOfEnumerator("Fluent_IconType")); for (int i = 0; i <= enumType.keyCount() - 1; ++i) { diff --git a/src/FluApp.h b/src/FluApp.h index 89934208..f691af61 100644 --- a/src/FluApp.h +++ b/src/FluApp.h @@ -36,7 +36,7 @@ SINGLETON(FluApp) Q_INVOKABLE void init(QObject *target, QLocale locale = QLocale::system()); - [[maybe_unused]] Q_INVOKABLE static QJsonArray iconDatas(const QString &keyword = ""); + [[maybe_unused]] Q_INVOKABLE static QJsonArray iconData(const QString &keyword = ""); private: QQmlEngine *_engine{}; diff --git a/src/FluFrameless.cpp b/src/FluFrameless.cpp index ec423f8a..8fec5f5e 100644 --- a/src/FluFrameless.cpp +++ b/src/FluFrameless.cpp @@ -35,16 +35,14 @@ static inline bool isCompositionEnabled() { return false; } -static inline void setShadow(HWND hwnd){ - const MARGINS shadow = { 1, 0, 0, 0 }; - typedef HRESULT (WINAPI* DwmExtendFrameIntoClientAreaPtr)(HWND hWnd, const MARGINS *pMarInset); +static inline void setShadow(HWND hwnd) { + const MARGINS shadow = {1, 0, 0, 0}; + typedef HRESULT (WINAPI *DwmExtendFrameIntoClientAreaPtr)(HWND hWnd, const MARGINS *pMarInset); HMODULE module = LoadLibraryW(L"dwmapi.dll"); - if (module) - { + if (module) { DwmExtendFrameIntoClientAreaPtr dwm_extendframe_into_client_area_; - dwm_extendframe_into_client_area_= reinterpret_cast(GetProcAddress(module, "DwmExtendFrameIntoClientArea")); - if (dwm_extendframe_into_client_area_) - { + dwm_extendframe_into_client_area_ = reinterpret_cast(GetProcAddress(module, "DwmExtendFrameIntoClientArea")); + if (dwm_extendframe_into_client_area_) { dwm_extendframe_into_client_area_(hwnd, &shadow); } } @@ -108,21 +106,21 @@ void FluFrameless::componentComplete() { HWND hwnd = reinterpret_cast(window()->winId()); DWORD style = ::GetWindowLongPtr(hwnd, GWL_STYLE); if (_fixSize) { - ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_CAPTION); + ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_CAPTION); for (int i = 0; i <= QGuiApplication::screens().count() - 1; ++i) { connect(QGuiApplication::screens().at(i), &QScreen::logicalDotsPerInchChanged, this, [=] { SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_FRAMECHANGED); }); } } else { - ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_CAPTION); + ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_CAPTION); } SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); connect(window(), &QQuickWindow::screenChanged, this, [hwnd] { ::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOOWNERZORDER); ::RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW); }); - if(!window()->property("_hideShadow").toBool()){ + if (!window()->property("_hideShadow").toBool()) { setShadow(hwnd); } #endif @@ -175,12 +173,12 @@ void FluFrameless::componentComplete() { *result = static_cast(hitTestResult); return true; } - if (!isMaximum){ + if (!isMaximum) { clientRect->top = originalTop; clientRect->bottom = originalBottom; clientRect->left = originalLeft; clientRect->right = originalRight; - } else{ + } else { clientRect->top = 0; } _setMaximizeHovered(false); @@ -240,13 +238,13 @@ void FluFrameless::componentComplete() { *result = HTCLIENT; return true; } else if (uMsg == WM_NCPAINT) { - if(isCompositionEnabled()){ + if (isCompositionEnabled()) { return false; } *result = FALSE; return true; } else if (uMsg == WM_NCACTIVATE) { - if(isCompositionEnabled()){ + if (isCompositionEnabled()) { return false; } *result = TRUE; @@ -362,27 +360,27 @@ void FluFrameless::_setMaximizeHovered(bool val) { void FluFrameless::_updateCursor(int edges) { switch (edges) { - case 0: - window()->setCursor(Qt::ArrowCursor); - break; - case Qt::LeftEdge: - case Qt::RightEdge: - window()->setCursor(Qt::SizeHorCursor); - break; - case Qt::TopEdge: - case Qt::BottomEdge: - window()->setCursor(Qt::SizeVerCursor); - break; - case Qt::LeftEdge | Qt::TopEdge: - case Qt::RightEdge | Qt::BottomEdge: - window()->setCursor(Qt::SizeFDiagCursor); - break; - case Qt::RightEdge | Qt::TopEdge: - case Qt::LeftEdge | Qt::BottomEdge: - window()->setCursor(Qt::SizeBDiagCursor); - break; - default: - break; + case 0: + window()->setCursor(Qt::ArrowCursor); + break; + case Qt::LeftEdge: + case Qt::RightEdge: + window()->setCursor(Qt::SizeHorCursor); + break; + case Qt::TopEdge: + case Qt::BottomEdge: + window()->setCursor(Qt::SizeVerCursor); + break; + case Qt::LeftEdge | Qt::TopEdge: + case Qt::RightEdge | Qt::BottomEdge: + window()->setCursor(Qt::SizeFDiagCursor); + break; + case Qt::RightEdge | Qt::TopEdge: + case Qt::LeftEdge | Qt::BottomEdge: + window()->setCursor(Qt::SizeBDiagCursor); + break; + default: + break; } } diff --git a/src/FluTableModel.cpp b/src/FluTableModel.cpp index ac74bd67..1a0e9c7c 100644 --- a/src/FluTableModel.cpp +++ b/src/FluTableModel.cpp @@ -1,6 +1,6 @@ #include "FluTableModel.h" -FluTableModel::FluTableModel(QObject *parent) : QAbstractTableModel{parent}{ +FluTableModel::FluTableModel(QObject *parent) : QAbstractTableModel{parent} { } @@ -14,20 +14,20 @@ int FluTableModel::columnCount(const QModelIndex &parent) const { QVariant FluTableModel::data(const QModelIndex &index, int role) const { switch (role) { - case FluTableModel::RowModel: - return QVariant::fromValue(_rows.at(index.row())); - case FluTableModel::ColumnModel: - return QVariant::fromValue(_columnSource.at(index.column())); - default: - break; + case FluTableModel::RowModel: + return QVariant::fromValue(_rows.at(index.row())); + case FluTableModel::ColumnModel: + return QVariant::fromValue(_columnSource.at(index.column())); + default: + break; } return {}; } QHash FluTableModel::roleNames() const { return { - {FluTableModel::RowModel, "rowModel"}, - {FluTableModel::ColumnModel, "columnModel"} + {FluTableModel::RowModel, "rowModel"}, + {FluTableModel::ColumnModel, "columnModel"} }; } @@ -41,33 +41,33 @@ QModelIndex FluTableModel::index(int row, int column, const QModelIndex &parent) return createIndex(row, column); } -void FluTableModel::clear(){ +void FluTableModel::clear() { beginResetModel(); this->_rows.clear(); endResetModel(); } -QVariant FluTableModel::getRow(int rowIndex){ +QVariant FluTableModel::getRow(int rowIndex) { return _rows.at(rowIndex); } -void FluTableModel::setRow(int rowIndex,QVariant row){ - _rows.replace(rowIndex,row.toMap()); +void FluTableModel::setRow(int rowIndex, QVariant row) { + _rows.replace(rowIndex, row.toMap()); Q_EMIT dataChanged(index(rowIndex, 0), index(rowIndex, columnCount() - 1)); } -void FluTableModel::insertRow(int rowIndex,QVariant row){ +void FluTableModel::insertRow(int rowIndex, QVariant row) { beginInsertRows(QModelIndex(), rowIndex, rowIndex); - _rows.insert(rowIndex,row.toMap()); + _rows.insert(rowIndex, row.toMap()); endInsertRows(); } -void FluTableModel::removeRow(int rowIndex,int rows){ +void FluTableModel::removeRow(int rowIndex, int rows) { beginRemoveRows(QModelIndex(), rowIndex, rowIndex + rows - 1); _rows = _rows.mid(0, rowIndex) + _rows.mid(rowIndex + rows); endRemoveRows(); } -void FluTableModel::appendRow(QVariant row){ - insertRow(rowCount(),row); +void FluTableModel::appendRow(QVariant row) { + insertRow(rowCount(), row); } diff --git a/src/FluTableModel.h b/src/FluTableModel.h index 15b72435..e2cf8879 100644 --- a/src/FluTableModel.h +++ b/src/FluTableModel.h @@ -7,10 +7,10 @@ #include "stdafx.h" class FluTableModel : public QAbstractTableModel { - Q_OBJECT - Q_PROPERTY_AUTO(int, dataSourceSize) - Q_PROPERTY_AUTO(QList, columnSource) - Q_PROPERTY_AUTO(QList, rows) +Q_OBJECT +Q_PROPERTY_AUTO(int, dataSourceSize) +Q_PROPERTY_AUTO(QList, columnSource) +Q_PROPERTY_AUTO(QList, rows) Q_PROPERTY(int rowCount READ rowCount CONSTANT) QML_NAMED_ELEMENT(FluTableModel) public: @@ -37,11 +37,11 @@ public: Q_INVOKABLE QVariant getRow(int rowIndex); - Q_INVOKABLE void setRow(int rowIndex,QVariant row); + Q_INVOKABLE void setRow(int rowIndex, QVariant row); - Q_INVOKABLE void insertRow(int rowIndex,QVariant row); + Q_INVOKABLE void insertRow(int rowIndex, QVariant row); - Q_INVOKABLE void removeRow(int rowIndex,int rows = 1); + Q_INVOKABLE void removeRow(int rowIndex, int rows = 1); Q_INVOKABLE void appendRow(QVariant row); diff --git a/src/FluTableSortProxyModel.cpp b/src/FluTableSortProxyModel.cpp index 1723a678..796c8113 100644 --- a/src/FluTableSortProxyModel.cpp +++ b/src/FluTableSortProxyModel.cpp @@ -3,9 +3,8 @@ #include FluTableSortProxyModel::FluTableSortProxyModel(QSortFilterProxyModel *parent) : QSortFilterProxyModel{parent} { - _model = nullptr; connect(this, &FluTableSortProxyModel::modelChanged, this, [=] { - setSourceModel(this->model()); + setSourceModel(this->model().value()); }); } @@ -59,19 +58,18 @@ bool FluTableSortProxyModel::lessThan(const QModelIndex &source_left, const QMod [[maybe_unused]] QVariant FluTableSortProxyModel::getRow(int rowIndex) { QVariant result; - QMetaObject::invokeMethod(_model, "getRow", Q_RETURN_ARG(QVariant, result), Q_ARG(int, mapToSource(index(rowIndex, 0)).row())); + QMetaObject::invokeMethod(_model.value(), "getRow", Q_RETURN_ARG(QVariant, result), Q_ARG(int, mapToSource(index(rowIndex, 0)).row())); return result; } [[maybe_unused]] void FluTableSortProxyModel::setRow(int rowIndex, const QVariant &val) { - QMetaObject::invokeMethod(_model, "setRow", Q_ARG(int, mapToSource(index(rowIndex, 0)).row()), Q_ARG(QVariant, val)); + QMetaObject::invokeMethod(_model.value(), "setRow", Q_ARG(int, mapToSource(index(rowIndex, 0)).row()), Q_ARG(QVariant, val)); } [[maybe_unused]] void FluTableSortProxyModel::insertRow(int rowIndex, const QVariant &val) { - QMetaObject::invokeMethod(_model, "insertRow", Q_ARG(int, mapToSource(index(rowIndex, 0)).row()), Q_ARG(QVariant, val)); + QMetaObject::invokeMethod(_model.value(), "insertRow", Q_ARG(int, mapToSource(index(rowIndex, 0)).row()), Q_ARG(QVariant, val)); } [[maybe_unused]] void FluTableSortProxyModel::removeRow(int rowIndex, int rows) { - QMetaObject::invokeMethod(_model, "removeRow", Q_ARG(int, mapToSource(index(rowIndex, 0)).row()), Q_ARG(int, rows)); + QMetaObject::invokeMethod(_model.value(), "removeRow", Q_ARG(int, mapToSource(index(rowIndex, 0)).row()), Q_ARG(int, rows)); } - diff --git a/src/FluTableSortProxyModel.h b/src/FluTableSortProxyModel.h index 05fb1d9e..319dc118 100644 --- a/src/FluTableSortProxyModel.h +++ b/src/FluTableSortProxyModel.h @@ -8,7 +8,7 @@ class FluTableSortProxyModel : public QSortFilterProxyModel { Q_OBJECT -Q_PROPERTY_AUTO_P(QAbstractTableModel*, model) +Q_PROPERTY_AUTO_P(QVariant, model) QML_NAMED_ELEMENT(FluTableSortProxyModel) public: explicit FluTableSortProxyModel(QSortFilterProxyModel *parent = nullptr); diff --git a/src/FluTools.h b/src/FluTools.h index 2ba82887..a044c5d5 100644 --- a/src/FluTools.h +++ b/src/FluTools.h @@ -95,5 +95,5 @@ SINGLETON(FluTools) Q_INVOKABLE QString getWallpaperFilePath(); - Q_INVOKABLE QColor imageMainColor(const QImage& image, double bright = 1); + Q_INVOKABLE QColor imageMainColor(const QImage &image, double bright = 1); }; diff --git a/src/FluTreeModel.cpp b/src/FluTreeModel.cpp index 44b3ae2e..baaaf010 100644 --- a/src/FluTreeModel.cpp +++ b/src/FluTreeModel.cpp @@ -264,12 +264,12 @@ void FluTreeModel::allCollapse() { endResetModel(); } -QVariant FluTreeModel::selectionModel(){ +QVariant FluTreeModel::selectionModel() { QList data; - foreach (auto item, _dataSource) { - if (item->checked()) { - data.append(item); + foreach (auto item, _dataSource) { + if (item->checked()) { + data.append(item); + } } - } return QVariant::fromValue(data); } diff --git a/src/FluTreeModel.h b/src/FluTreeModel.h index 405e766c..0fc1b53c 100644 --- a/src/FluTreeModel.h +++ b/src/FluTreeModel.h @@ -50,7 +50,7 @@ public: } return true; }; - + Q_INVOKABLE bool hideLineFooter() { if (_parent) { auto childIndex = _parent->_children.indexOf(this); diff --git a/src/Qt5/imports/FluentUI/Controls/FluShadow.qml b/src/Qt5/imports/FluentUI/Controls/FluShadow.qml index 34c95753..11406b7c 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluShadow.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluShadow.qml @@ -4,7 +4,7 @@ import FluentUI 1.0 Item { //高性能阴影!!!比DropShadow阴影性能高出数倍!!! - property color color: FluTheme.dark ? "#AAAAAA" : "#999999" + property color color: FluTheme.dark ? "#000000" : "#999999" property int elevation: 5 property int radius: 4 id:control diff --git a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml index 029c4e10..9385d810 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml @@ -9,7 +9,9 @@ Rectangle { readonly property alias rows: table_view.rows readonly property alias columns: table_view.columns readonly property alias current: d.current - readonly property alias sourceModel: table_model + property var sourceModel:FluTableModel { + columnSource: control.columnSource + } property var columnSource: [] property var dataSource property color borderColor: FluTheme.dark ? Qt.rgba(37/255,37/255,37/255,1) : Qt.rgba(228/255,228/255,228/255,1) @@ -29,7 +31,7 @@ Rectangle { var columns= [] var headerRow = {} columnSource.forEach(function(item){ - var column = Qt.createQmlObject('import Qt.labs.qmlmodels 1.0;TableModelColumn{}',table_model); + var column = Qt.createQmlObject('import Qt.labs.qmlmodels 1.0;TableModelColumn{}',sourceModel); column.display = item.dataIndex columns.push(column) headerRow[item.dataIndex] = item.title @@ -58,12 +60,8 @@ Rectangle { } } onDataSourceChanged: { - table_model.clear() - table_model.rows = dataSource - } - FluTableModel { - id:table_model - columnSource: control.columnSource + sourceModel.clear() + sourceModel.rows = dataSource } TableModel{ id:header_column_model @@ -75,7 +73,7 @@ Rectangle { } FluTableSortProxyModel{ id:table_sort_model - model: table_model + model: control.sourceModel } Component{ id:com_edit @@ -191,6 +189,7 @@ Rectangle { id:com_table_delegate MouseArea{ id:item_table_mouse + property var _model: model property bool isRowSelected: { if(rowModel === null) return false @@ -283,6 +282,7 @@ Rectangle { } FluLoader{ id: item_table_loader + property var model: item_table_mouse._model property var display: rowModel[columnModel.dataIndex] property var rowModel : model.rowModel property var columnModel : model.columnModel @@ -446,7 +446,7 @@ Rectangle { id:column_item_control readonly property real cellPadding: 8 property bool canceled: false - property int columnIndex: column + property var _model: model readonly property var columnObject : control.columnSource[column] implicitWidth: { return (item_column_loader.item && item_column_loader.item.implicitWidth) + (cellPadding * 2) @@ -503,22 +503,23 @@ Rectangle { } FluLoader{ id:item_column_loader - property var itemModel: model - property var modelData: model.display + property var model: column_item_control._model + property var display: model.display property var tableView: table_view - property var tableModel: table_model + property var sourceModel: control.sourceModel + property bool isObject: typeof(display) == "object" property var options:{ - if(typeof(modelData) == "object"){ - return modelData.options + if(isObject){ + return display.options } return {} } - property int column: column_item_control.columnIndex + property int column: model.column width: parent.width height: parent.height sourceComponent: { - if(typeof(modelData) == "object"){ - return modelData.comId + if(isObject){ + return display.comId } return com_column_text } @@ -686,7 +687,7 @@ Rectangle { id:com_column_text FluText { id: column_text - text: modelData + text: String(display) anchors.fill: parent horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter @@ -835,7 +836,7 @@ Rectangle { function sort(callback=undefined){ if(callback){ table_sort_model.setComparator(function(left,right){ - return callback(table_model.getRow(left),table_model.getRow(right)) + return callback(sourceModel.getRow(left),sourceModel.getRow(right)) }) }else{ table_sort_model.setComparator(undefined) @@ -844,7 +845,7 @@ Rectangle { function filter(callback=undefined){ if(callback){ table_sort_model.setFilter(function(index){ - return callback(table_model.getRow(index)) + return callback(sourceModel.getRow(index)) }) }else{ table_sort_model.setFilter(undefined) @@ -868,7 +869,7 @@ Rectangle { } function insertRow(rowIndex,obj){ if(rowIndex>=0 && rowIndex=0 && rowIndex Date: Sun, 28 Apr 2024 21:05:43 +0800 Subject: [PATCH 05/35] update --- example/example_en_US.ts | 12 ++++-------- example/example_zh_CN.ts | 10 +++++----- example/qml/page/T_Icons.qml | 15 +++------------ example/qml/page/T_Timeline.qml | 1 - .../imports/FluentUI/Controls/FluTableView.qml | 2 +- .../imports/FluentUI/Controls/FluTableView.qml | 2 +- 6 files changed, 14 insertions(+), 28 deletions(-) diff --git a/example/example_en_US.ts b/example/example_en_US.ts index 4ac68c4d..1d9f801f 100644 --- a/example/example_en_US.ts +++ b/example/example_en_US.ts @@ -1292,6 +1292,7 @@ My only desire is to be permitted to drive out the traitors and restore the Han. RadioButton Group + RadioButton Group111111111111111111111111 @@ -1331,12 +1332,7 @@ My only desire is to be permitted to drive out the traitors and restore the Han. - - Search - - - - + You Copied @@ -2203,12 +2199,12 @@ Some contents... - + Append - + clear diff --git a/example/example_zh_CN.ts b/example/example_zh_CN.ts index 5880157d..fb141605 100644 --- a/example/example_zh_CN.ts +++ b/example/example_zh_CN.ts @@ -1378,6 +1378,7 @@ My only desire is to be permitted to drive out the traitors and restore the Han. RadioButton Group + RadioButton Group111111111111111111111111 单选框分组 @@ -1417,12 +1418,11 @@ My only desire is to be permitted to drive out the traitors and restore the Han. 请输入关键字 - Search - 搜索 + 搜索 - + You Copied 您复制 @@ -2345,12 +2345,12 @@ Some contents... 时间轴 - + Append 追加 - + clear 清空 diff --git a/example/qml/page/T_Icons.qml b/example/qml/page/T_Icons.qml index 8cf6a584..cb078ce5 100644 --- a/example/qml/page/T_Icons.qml +++ b/example/qml/page/T_Icons.qml @@ -14,17 +14,8 @@ FluContentPage { anchors{ top: parent.top } - } - - FluFilledButton{ - text: qsTr("Search") - anchors{ - left: text_box.right - verticalCenter: text_box.verticalCenter - leftMargin: 14 - } - onClicked: { - grid_view.model = FluApp.iconDatas(text_box.text) + onTextChanged: { + grid_view.model = FluApp.iconData(text_box.text) } } GridView{ @@ -33,7 +24,7 @@ FluContentPage { cellHeight: 110 clip: true boundsBehavior: GridView.StopAtBounds - model: FluApp.iconDatas() + model: FluApp.iconData() ScrollBar.vertical: FluScrollBar {} anchors{ topMargin: 10 diff --git a/example/qml/page/T_Timeline.qml b/example/qml/page/T_Timeline.qml index 49800109..d69f0feb 100644 --- a/example/qml/page/T_Timeline.qml +++ b/example/qml/page/T_Timeline.qml @@ -44,7 +44,6 @@ FluScrollablePage{ wrapMode: Text.WrapAnywhere horizontalAlignment: isRight ? Qt.AlignRight : Qt.AlignLeft text: modelData.text - font.bold: true linkColor: FluTheme.dark ? FluColors.Teal.lighter : FluColors.Teal.dark onLinkActivated: (link)=> { diff --git a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml index 9385d810..2f19e373 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml @@ -253,7 +253,7 @@ Rectangle { if(d.rowHoverIndex === row || item_table_mouse.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)) + return (row%2!==0) ? control.color : (FluTheme.dark ? Qt.rgba(1,1,1,0.03) : Qt.rgba(0,0,0,0.03)) } MouseArea{ anchors.fill: parent diff --git a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml index 0a56ac94..2872fd71 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml @@ -253,7 +253,7 @@ Rectangle { if(d.rowHoverIndex === row || item_table_mouse.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)) + return (row%2!==0) ? control.color : (FluTheme.dark ? Qt.rgba(1,1,1,0.03) : Qt.rgba(0,0,0,0.03)) } MouseArea{ anchors.fill: parent From 30531079b5119e9ffdce6cc025aa488e7a15c019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Mon, 29 Apr 2024 10:12:34 +0800 Subject: [PATCH 06/35] update --- example/example_en_US.ts | 14 +- example/example_zh_CN.ts | 14 +- example/qml/page/T_TableView.qml | 10 +- .../FluentUI/Controls/FluTableView.qml | 153 ++++++++++++++---- 4 files changed, 144 insertions(+), 47 deletions(-) diff --git a/example/example_en_US.ts b/example/example_en_US.ts index 1d9f801f..8c963611 100644 --- a/example/example_en_US.ts +++ b/example/example_en_US.ts @@ -1964,7 +1964,7 @@ Some contents... - + Name @@ -2019,32 +2019,32 @@ Some contents... - + Address - + Nickname - + Long String - + Options - + <Previous - + Next> diff --git a/example/example_zh_CN.ts b/example/example_zh_CN.ts index fb141605..7c824dda 100644 --- a/example/example_zh_CN.ts +++ b/example/example_zh_CN.ts @@ -2110,7 +2110,7 @@ Some contents... - + Name 名称 @@ -2155,32 +2155,32 @@ Some contents... 头像 - + Address 地址 - + Nickname 昵称 - + Long String 长字符串 - + Options 操作 - + <Previous <上一页 - + Next> 下一页> diff --git a/example/qml/page/T_TableView.qml b/example/qml/page/T_TableView.qml index fe0f6e93..c70b09d1 100644 --- a/example/qml/page/T_TableView.qml +++ b/example/qml/page/T_TableView.qml @@ -499,12 +499,15 @@ FluContentPage{ dataIndex: 'checkbox', width:100, minimumWidth:100, - maximumWidth:100 + maximumWidth:300 }, { title: table_view.customItem(com_column_update_title,{title:qsTr("Avatar")}), dataIndex: 'avatar', - width:100 + width:100, + minimumWidth:100, + maximumWidth:100, + frozen:true }, { title: table_view.customItem(com_column_filter_name,{title:qsTr("Name")}), @@ -547,7 +550,8 @@ FluContentPage{ dataIndex: 'action', width:160, minimumWidth:160, - maximumWidth:160 + maximumWidth:160, + frozen:true } ] } diff --git a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml index 2872fd71..21eba096 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml @@ -30,12 +30,20 @@ Rectangle { if(columnSource.length!==0){ var columns= [] var headerRow = {} - columnSource.forEach(function(item){ + var offsetX = 0 + for(var i=0;i<=columnSource.length-1;i++){ + var item = columnSource[i] + var w = item.width + if(!w){ + w = d.defaultItemWidth + } + item.x = offsetX + offsetX = offsetX + w var column = Qt.createQmlObject('import Qt.labs.qmlmodels 1.0;TableModelColumn{}',sourceModel); column.display = item.dataIndex columns.push(column) headerRow[item.dataIndex] = item.title - }) + } header_column_model.columns = columns header_column_model.rows = [headerRow] } @@ -48,6 +56,7 @@ Rectangle { property int defaultItemHeight: 42 property var editDelegate property var editPosition + signal tableItemLayout(int column) function getEditDelegate(column){ var obj =control.columnSource[column].editDelegate if(obj){ @@ -189,6 +198,7 @@ Rectangle { id:com_table_delegate MouseArea{ id:item_table_mouse + implicitWidth: TableView.view.width property var _model: model property bool isRowSelected: { if(rowModel === null) @@ -198,17 +208,17 @@ Rectangle { } return false } - TableView.onPooled: { - if(d.editPosition && d.editPosition.row === row && d.editPosition.column === column){ - control.closeEditor() - } - } property bool editVisible: { if(d.editPosition && d.editPosition._key === rowModel._key && d.editPosition.column === column){ return true } return false } + TableView.onPooled: { + if(d.editPosition && d.editPosition.row === row && d.editPosition.column === column){ + control.closeEditor() + } + } hoverEnabled: true onEntered: { d.rowHoverIndex = row @@ -217,21 +227,25 @@ Rectangle { if(editVisible){ updateEditPosition() } + item_table_mouse.updateTableItem() } onHeightChanged: { if(editVisible){ updateEditPosition() } + item_table_mouse.updateTableItem() } onXChanged: { if(editVisible){ updateEditPosition() } + item_table_mouse.updateTableItem() } onYChanged: { if(editVisible){ updateEditPosition() } + item_table_mouse.updateTableItem() } function updateEditPosition(){ var obj = {} @@ -244,6 +258,12 @@ Rectangle { obj.height = item_table_mouse.height - 2 d.editPosition = obj } + function updateTableItem(){ + var columnModel = control.columnSource[column] + columnModel.x = item_table_mouse.x + columnModel.y = item_table_mouse.y + d.tableItemLayout(column) + } Rectangle{ anchors.fill: parent color:{ @@ -271,7 +291,7 @@ Rectangle { } loader_edit.display = item_table_loader.display d.editDelegate = d.getEditDelegate(column) - updateEditPosition() + item_table_mouse.updateEditPosition() } onClicked: (event)=>{ @@ -358,24 +378,24 @@ Rectangle { ScrollBar.horizontal:scroll_bar_h ScrollBar.vertical:scroll_bar_v columnWidthProvider: function(column) { - var columnObject = control.columnSource[column] - var width = columnObject.width + var columnModel = control.columnSource[column] + var width = columnModel.width if(width){ return width } - var minimumWidth = columnObject.minimumWidth + var minimumWidth = columnModel.minimumWidth if(minimumWidth){ return minimumWidth } return d.defaultItemWidth } rowHeightProvider: function(row) { - var rowObject = control.getRow(row) - var height = rowObject.height + var rowModel = control.getRow(row) + var height = rowModel.height if(height){ return height } - var minimumHeight = rowObject._minimumHeight + var minimumHeight = rowModel._minimumHeight if(minimumHeight){ return minimumHeight } @@ -440,6 +460,7 @@ Rectangle { } } } + Component{ id:com_column_header_delegate Rectangle{ @@ -447,7 +468,7 @@ Rectangle { readonly property real cellPadding: 8 property bool canceled: false property var _model: model - readonly property var columnObject : control.columnSource[column] + readonly property var columnModel : control.columnSource[column] implicitWidth: { return (item_column_loader.item && item_column_loader.item.implicitWidth) + (cellPadding * 2) } @@ -507,7 +528,7 @@ Rectangle { property var display: model.display property var tableView: table_view property var sourceModel: control.sourceModel - property bool isObject: typeof(display) == "object" + property bool isObject: typeof(display) == "object" property var options:{ if(isObject){ return display.options @@ -531,7 +552,7 @@ Rectangle { anchors.right: parent.right acceptedButtons: Qt.LeftButton hoverEnabled: true - visible: !(columnObject.width === columnObject.minimumWidth && columnObject.width === columnObject.maximumWidth && columnObject.width) + visible: !(columnModel.width === columnModel.minimumWidth && columnModel.width === columnModel.maximumWidth && columnModel.width) cursorShape: Qt.SplitHCursor preventStealing: true onPressed : @@ -551,9 +572,9 @@ Rectangle { 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 + var minimumWidth = columnModel.minimumWidth + var maximumWidth = columnModel.maximumWidth + var w = columnModel.width if(!w){ w = d.defaultItemWidth } @@ -563,7 +584,7 @@ Rectangle { if(!maximumWidth){ maximumWidth = 65535 } - columnObject.width = Math.min(Math.max(minimumWidth, w + delta.x),maximumWidth) + columnModel.width = Math.min(Math.max(minimumWidth, w + delta.x),maximumWidth) table_view.forceLayout() header_horizontal.forceLayout() } @@ -576,7 +597,7 @@ Rectangle { id:item_control readonly property real cellPadding: 8 property bool canceled: false - property var rowObject: control.getRow(row) + property var rowModel: control.getRow(row) implicitWidth: Math.max(30, row_text.implicitWidth + (cellPadding * 2)) implicitHeight: row_text.implicitHeight + (cellPadding * 2) color: FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(247/255,247/255,247/255,1) @@ -642,9 +663,9 @@ Rectangle { cursorShape: Qt.SplitVCursor preventStealing: true visible: { - if(rowObject === null) + if(rowModel === null) return false - return !(rowObject.height === rowObject._minimumHeight && rowObject.height === rowObject._maximumHeight && rowObject.height) + return !(rowModel.height === rowModel._minimumHeight && rowModel.height === rowModel._maximumHeight && rowModel.height) } onPressed : (mouse)=>{ @@ -662,11 +683,11 @@ Rectangle { if(!pressed){ return } - var rowObject = control.getRow(row) + var rowModel = control.getRow(row) var delta = Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y) - var minimumHeight = rowObject._minimumHeight - var maximumHeight = rowObject._maximumHeight - var h = rowObject.height + var minimumHeight = rowModel._minimumHeight + var maximumHeight = rowModel._maximumHeight + var h = rowModel.height if(!h){ h = d.defaultItemHeight } @@ -676,8 +697,8 @@ Rectangle { if(!maximumHeight){ maximumHeight = 65535 } - rowObject.height = Math.min(Math.max(minimumHeight, h + delta.y),maximumHeight) - control.setRow(row,rowObject) + rowModel.height = Math.min(Math.max(minimumHeight, h + delta.y),maximumHeight) + control.setRow(row,rowModel) table_view.forceLayout() } } @@ -790,6 +811,78 @@ Rectangle { } } } + + Item{ + anchors{ + left: header_vertical.right + top: parent.top + bottom: parent.bottom + } + + Component{ + id: com_table_frozen + Item{ + id: item_layout_frozen + property var sourceModel:FluTableModel { + columnSource: columnModel + rows: control.sourceModel.rows + } + TableView{ + clip: true + anchors{ + fill: parent + topMargin: header_horizontal.height + } + model: control.sourceModel + delegate: com_table_delegate + syncDirection: Qt.Vertical + syncView: table_view + } + } + } + + Repeater{ + model: control.columnSource + delegate: FluLoader{ + id: item_layout_frozen + readonly property var columnModel : control.columnSource[model.index] + readonly property int index : model.index + width: table_view.columnWidthProvider(index) + Connections{ + target: d + function onTableItemLayout(column){ + if(column === index){ + item_layout_frozen.updateLayout() + } + } + } + Connections{ + target: table_view + function onContentXChanged(){ + item_layout_frozen.updateLayout() + } + } + function updateLayout(){ + width = table_view.columnWidthProvider(index) + x = Qt.binding(function(){ return Math.min(Math.max(columnModel.x - table_view.contentX,0),table_view.width-item_layout_frozen.width) }) + } + Component.onCompleted: { + updateLayout() + } + height: control.height + visible: { + if(modelData.frozen){ + return true + } + return false + } + sourceComponent: visible ? com_table_frozen : undefined + } + } + } + + + FluScrollBar { id: scroll_bar_h anchors{ From bf001d99d2f28095d635e05dd74e5eaed4273f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Tue, 30 Apr 2024 12:59:09 +0800 Subject: [PATCH 07/35] update --- example/example_en_US.ts | 28 +- example/example_zh_CN.ts | 28 +- example/qml/page/T_TableView.qml | 31 +- example/qml/window/MainWindow.qml | 2 +- .../imports/FluentUI/Controls/FluComboBox.qml | 2 + .../FluentUI/Controls/FluScrollBar.qml | 2 +- .../FluentUI/Controls/FluTableView.qml | 352 +++++++++++++----- .../imports/FluentUI/Controls/FluWindow.qml | 1 - .../imports/FluentUI/Controls/FluComboBox.qml | 2 + .../FluentUI/Controls/FluScrollBar.qml | 2 +- .../FluentUI/Controls/FluTableView.qml | 257 ++++++++----- .../imports/FluentUI/Controls/FluWindow.qml | 1 - 12 files changed, 474 insertions(+), 234 deletions(-) diff --git a/example/example_en_US.ts b/example/example_en_US.ts index 8c963611..7ed2f842 100644 --- a/example/example_en_US.ts +++ b/example/example_en_US.ts @@ -1964,7 +1964,7 @@ Some contents... - + Name @@ -1984,67 +1984,67 @@ Some contents... - + Age - + Clear All - + Delete Selection - + Add a row of Data - + Insert a Row - + Focus not acquired: Please click any item in the form as the target for insertion! - + Avatar - + Address - + Nickname - + Long String - + Options - + <Previous - + Next> diff --git a/example/example_zh_CN.ts b/example/example_zh_CN.ts index 7c824dda..de111fa9 100644 --- a/example/example_zh_CN.ts +++ b/example/example_zh_CN.ts @@ -2110,7 +2110,7 @@ Some contents... - + Name 名称 @@ -2130,67 +2130,67 @@ Some contents... 全选 - + Age 年龄 - + Clear All 清除所有 - + Insert a Row 插入一行 - + Focus not acquired: Please click any item in the form as the target for insertion! 焦点未获取:请点击表格中的任意一项,作为插入的靶点! - + Avatar 头像 - + Address 地址 - + Nickname 昵称 - + Long String 长字符串 - + Options 操作 - + <Previous <上一页 - + Next> 下一页> - + Delete Selection 删除选中 - + Add a row of Data 添加一行数据 diff --git a/example/qml/page/T_TableView.qml b/example/qml/page/T_TableView.qml index c70b09d1..962afb3a 100644 --- a/example/qml/page/T_TableView.qml +++ b/example/qml/page/T_TableView.qml @@ -244,7 +244,9 @@ FluContentPage{ clickListener: function(){ root.selectedAll = !root.selectedAll var checked = root.selectedAll - model.display = table_view.customItem(com_column_checbox,{"checked":checked}) + var columnModel = model.display + columnModel.title = table_view.customItem(com_column_checbox,{"checked":checked}) + model.display = columnModel for(var i =0;i< table_view.rows ;i++){ var rowData = table_view.getRow(i) rowData.checkbox = table_view.customItem(com_checbox,{"checked":checked}) @@ -271,7 +273,9 @@ FluContentPage{ } Component.onCompleted: { currentIndex=["100","300","500","1000"].findIndex((element) => element === display) - selectAll() + console.debug(textBox) + textBox.forceActiveFocus() + textBox.selectAll() } onCommit: { editTextChaged(editText) @@ -287,12 +291,14 @@ FluContentPage{ anchors.fill: parent focus: true Component.onCompleted: { + console.debug("333333") var data = ["傲来国界花果山水帘洞","傲来国界坎源山脏水洞","大唐国界黑风山黑风洞","大唐国界黄风岭黄风洞","大唐国界骷髅山白骨洞","宝象国界碗子山波月洞","宝象国界平顶山莲花洞","宝象国界压龙山压龙洞","乌鸡国界号山枯松涧火云洞","乌鸡国界衡阳峪黑水河河神府"] var result = data.map(function(item) { return {title: item}; }); items = result textbox.text= String(display) + forceActiveFocus() selectAll() } onCommit: { @@ -345,7 +351,9 @@ FluContentPage{ cursorShape: Qt.PointingHandCursor onClicked: { custom_update_dialog.showDialog(options.title,function(text){ - itemModel.display = table_view.customItem(com_column_update_title,{"title":text}) + var columnModel = model.display + columnModel.title = table_view.customItem(com_column_update_title,{"title":text}) + model.display = columnModel }) } } @@ -499,21 +507,20 @@ FluContentPage{ dataIndex: 'checkbox', width:100, minimumWidth:100, - maximumWidth:300 - }, - { - title: table_view.customItem(com_column_update_title,{title:qsTr("Avatar")}), - dataIndex: 'avatar', - width:100, - minimumWidth:100, - maximumWidth:100, - frozen:true + maximumWidth:300, + frozen: true }, { title: table_view.customItem(com_column_filter_name,{title:qsTr("Name")}), dataIndex: 'name', readOnly:true }, + { + title: table_view.customItem(com_column_update_title,{title:qsTr("Avatar")}), + dataIndex: 'avatar', + width:100, + frozen:true + }, { title: table_view.customItem(com_column_sort_age,{sort:0}), dataIndex: 'age', diff --git a/example/qml/window/MainWindow.qml b/example/qml/window/MainWindow.qml index 8b92bedc..4fff0309 100644 --- a/example/qml/window/MainWindow.qml +++ b/example/qml/window/MainWindow.qml @@ -15,7 +15,7 @@ FluWindow { title: "FluentUI" width: 1000 height: 680 - minimumWidth: 680 + minimumWidth: 1000 minimumHeight: 200 launchMode: FluWindowType.SingleTask fitsAppBarWindows: true diff --git a/src/Qt5/imports/FluentUI/Controls/FluComboBox.qml b/src/Qt5/imports/FluentUI/Controls/FluComboBox.qml index 4618f2fd..279fb0f3 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluComboBox.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluComboBox.qml @@ -11,6 +11,7 @@ T.ComboBox { property color normalColor: FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(254/255,254/255,254/255,1) property color hoverColor: FluTheme.dark ? Qt.rgba(68/255,68/255,68/255,1) : Qt.rgba(251/255,251/255,251/255,1) property color disableColor: FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(252/255,252/255,252/255,1) + property alias textBox: text_field implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, implicitContentWidth + leftPadding + rightPadding) implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, @@ -39,6 +40,7 @@ T.ComboBox { opacity: enabled ? 1 : 0.3 } contentItem: T.TextField { + id: text_field property bool disabled: !control.editable leftPadding: !control.mirrored ? 10 : control.editable && activeFocus ? 3 : 1 rightPadding: control.mirrored ? 10 : control.editable && activeFocus ? 3 : 1 diff --git a/src/Qt5/imports/FluentUI/Controls/FluScrollBar.qml b/src/Qt5/imports/FluentUI/Controls/FluScrollBar.qml index 2057963f..87953811 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluScrollBar.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluScrollBar.qml @@ -171,7 +171,7 @@ T.ScrollBar { ,Transition { to: "show" SequentialAnimation { - PauseAnimation { duration: 450 } + PauseAnimation { duration: 150 } NumberAnimation { target: rect_bar properties: vertical ? "width" : "height" diff --git a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml index 2f19e373..2e04312e 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml @@ -30,12 +30,19 @@ Rectangle { if(columnSource.length!==0){ var columns= [] var headerRow = {} - columnSource.forEach(function(item){ + var offsetX = 0 + for(var i=0;i<=columnSource.length-1;i++){ + var item = columnSource[i] + if(!item.width){ + item.width = d.defaultItemWidth + } + item.x = offsetX + offsetX = offsetX + item.width var column = Qt.createQmlObject('import Qt.labs.qmlmodels 1.0;TableModelColumn{}',sourceModel); column.display = item.dataIndex columns.push(column) - headerRow[item.dataIndex] = item.title - }) + headerRow[item.dataIndex] = item + } header_column_model.columns = columns header_column_model.rows = [headerRow] } @@ -48,6 +55,7 @@ Rectangle { property int defaultItemHeight: 42 property var editDelegate property var editPosition + signal tableItemLayout(int column) function getEditDelegate(column){ var obj =control.columnSource[column].editDelegate if(obj){ @@ -64,15 +72,15 @@ Rectangle { sourceModel.rows = dataSource } TableModel{ - id:header_column_model + id: header_column_model TableModelColumn { display : "title"} } TableModel{ - id:header_row_model + id: header_row_model TableModelColumn { display: "rowIndex" } } FluTableSortProxyModel{ - id:table_sort_model + id: table_sort_model model: control.sourceModel } Component{ @@ -189,7 +197,20 @@ Rectangle { id:com_table_delegate MouseArea{ id:item_table_mouse + implicitWidth: TableView.view.width property var _model: model + property bool isMainTable: TableView.view == table_view + property var currentTableView: TableView.view + visible: { + if(isMainTable && columnModel.frozen){ + return false + } + if(!isMainTable){ + if(currentTableView.dataIndex !== columnModel.dataIndex) + return false + } + return true + } property bool isRowSelected: { if(rowModel === null) return false @@ -198,17 +219,17 @@ Rectangle { } return false } - TableView.onPooled: { - if(d.editPosition && d.editPosition.row === row && d.editPosition.column === column){ - control.closeEditor() - } - } property bool editVisible: { if(d.editPosition && d.editPosition._key === rowModel._key && d.editPosition.column === column){ return true } return false } + TableView.onPooled: { + if(d.editPosition && d.editPosition.row === row && d.editPosition.column === column){ + control.closeEditor() + } + } hoverEnabled: true onEntered: { d.rowHoverIndex = row @@ -217,21 +238,33 @@ Rectangle { if(editVisible){ updateEditPosition() } + if(isMainTable){ + updateTableItem() + } } onHeightChanged: { if(editVisible){ updateEditPosition() } + if(isMainTable){ + updateTableItem() + } } onXChanged: { if(editVisible){ updateEditPosition() } + if(isMainTable){ + updateTableItem() + } } onYChanged: { if(editVisible){ updateEditPosition() } + if(isMainTable){ + updateTableItem() + } } function updateEditPosition(){ var obj = {} @@ -244,6 +277,12 @@ Rectangle { obj.height = item_table_mouse.height - 2 d.editPosition = obj } + function updateTableItem(){ + var columnModel = control.columnSource[column] + columnModel.x = item_table_mouse.x + columnModel.y = item_table_mouse.y + d.tableItemLayout(column) + } Rectangle{ anchors.fill: parent color:{ @@ -271,7 +310,7 @@ Rectangle { } loader_edit.display = item_table_loader.display d.editDelegate = d.getEditDelegate(column) - updateEditPosition() + item_table_mouse.updateEditPosition() } onClicked: (event)=>{ @@ -297,12 +336,50 @@ Rectangle { } anchors.fill: parent sourceComponent: { - if(isObject){ - return display.comId + if(item_table_mouse.visible){ + if(isObject){ + return display.comId + } + return com_text } - return com_text + return undefined } } + 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 + } + anchors{ + fill: parent + margins: 1 + } + signal editTextChaged(string text) + sourceComponent: { + if(item_table_mouse.visible && d.editPosition && d.editPosition.column === model.column && d.editPosition.row === model.row){ + return d.editDelegate + } + return undefined + } + onEditTextChaged: + (text)=>{ + var obj = control.getRow(row) + obj[control.columnSource[column].dataIndex] = text + control.setRow(row,obj) + } + z:999 + } Item{ anchors.fill: parent visible: item_table_mouse.isRowSelected @@ -358,24 +435,24 @@ Rectangle { ScrollBar.horizontal:scroll_bar_h ScrollBar.vertical:scroll_bar_v columnWidthProvider: function(column) { - var columnObject = control.columnSource[column] - var width = columnObject.width + var columnModel = control.columnSource[column] + var width = columnModel.width if(width){ return width } - var minimumWidth = columnObject.minimumWidth + var minimumWidth = columnModel.minimumWidth if(minimumWidth){ return minimumWidth } return d.defaultItemWidth } rowHeightProvider: function(row) { - var rowObject = control.getRow(row) - var height = rowObject.height + var rowModel = control.getRow(row) + var height = rowModel.height if(height){ return height } - var minimumHeight = rowObject._minimumHeight + var minimumHeight = rowModel._minimumHeight if(minimumHeight){ return minimumHeight } @@ -388,70 +465,37 @@ Rectangle { table_view.flick(0,1) } delegate: com_table_delegate - 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)=>{ - var obj = control.getRow(row) - obj[control.columnSource[column].dataIndex] = text - control.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 - } } } + Component{ id:com_column_header_delegate Rectangle{ - id:column_item_control + id: column_item_control + property var currentTableView : TableView.view readonly property real cellPadding: 8 property bool canceled: false property var _model: model - readonly property var columnObject : control.columnSource[column] - implicitWidth: { - return (item_column_loader.item && item_column_loader.item.implicitWidth) + (cellPadding * 2) + readonly property var columnModel : control.columnSource[_index] + readonly property int _index : { + const isDataIndex = (element) => { + return element.dataIndex === display.dataIndex + } + return control.columnSource.findIndex(isDataIndex) + } + readonly property bool isHeaderHorizontal: TableView.view == header_horizontal + implicitWidth: { + if(column_item_control.isHeaderHorizontal){ + return (item_column_loader.item && item_column_loader.item.implicitWidth) + (cellPadding * 2) + } + return TableView.view.width + } + implicitHeight: { + if(column_item_control.isHeaderHorizontal){ + return Math.max(36, (item_column_loader.item&&item_column_loader.item.implicitHeight) + (cellPadding * 2)) + } + return TableView.view.height } - 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 @@ -472,7 +516,7 @@ Rectangle { width: 1 height: parent.height anchors.left: parent.left - visible: column !== 0 + visible: column_item_control._index !== 0 color:"#00000000" } Rectangle{ @@ -481,7 +525,7 @@ Rectangle { height: parent.height anchors.right: parent.right color:"#00000000" - visible: column === table_view.columns - 1 + visible: column_item_control._index === table_view.columns - 1 } MouseArea{ id:column_item_control_mouse @@ -504,17 +548,17 @@ Rectangle { FluLoader{ id:item_column_loader property var model: column_item_control._model - property var display: model.display + property var display: model.display.title property var tableView: table_view property var sourceModel: control.sourceModel - property bool isObject: typeof(display) == "object" + property bool isObject: typeof(display) == "object" property var options:{ if(isObject){ return display.options } return {} } - property int column: model.column + property int column: column_item_control._index width: parent.width height: parent.height sourceComponent: { @@ -531,7 +575,7 @@ Rectangle { anchors.right: parent.right acceptedButtons: Qt.LeftButton hoverEnabled: true - visible: !(columnObject.width === columnObject.minimumWidth && columnObject.width === columnObject.maximumWidth && columnObject.width) + visible: !columnModel.frozen && !(columnModel.width === columnModel.minimumWidth && columnModel.width === columnModel.maximumWidth && columnModel.width) cursorShape: Qt.SplitHCursor preventStealing: true onPressed : @@ -551,9 +595,9 @@ Rectangle { 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 + var minimumWidth = columnModel.minimumWidth + var maximumWidth = columnModel.maximumWidth + var w = columnModel.width if(!w){ w = d.defaultItemWidth } @@ -563,9 +607,10 @@ Rectangle { if(!maximumWidth){ maximumWidth = 65535 } - columnObject.width = Math.min(Math.max(minimumWidth, w + delta.x),maximumWidth) + columnModel.width = Math.min(Math.max(minimumWidth, w + delta.x),maximumWidth) table_view.forceLayout() header_horizontal.forceLayout() +// column_item_control.currentTableView.forceLayout() } } } @@ -576,7 +621,7 @@ Rectangle { id:item_control readonly property real cellPadding: 8 property bool canceled: false - property var rowObject: control.getRow(row) + property var rowModel: control.getRow(row) implicitWidth: Math.max(30, row_text.implicitWidth + (cellPadding * 2)) implicitHeight: row_text.implicitHeight + (cellPadding * 2) color: FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(247/255,247/255,247/255,1) @@ -642,9 +687,9 @@ Rectangle { cursorShape: Qt.SplitVCursor preventStealing: true visible: { - if(rowObject === null) + if(rowModel === null) return false - return !(rowObject.height === rowObject._minimumHeight && rowObject.height === rowObject._maximumHeight && rowObject.height) + return !(rowModel.height === rowModel._minimumHeight && rowModel.height === rowModel._maximumHeight && rowModel.height) } onPressed : (mouse)=>{ @@ -662,11 +707,11 @@ Rectangle { if(!pressed){ return } - var rowObject = control.getRow(row) + var rowModel = control.getRow(row) var delta = Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y) - var minimumHeight = rowObject._minimumHeight - var maximumHeight = rowObject._maximumHeight - var h = rowObject.height + var minimumHeight = rowModel._minimumHeight + var maximumHeight = rowModel._maximumHeight + var h = rowModel.height if(!h){ h = d.defaultItemHeight } @@ -676,8 +721,8 @@ Rectangle { if(!maximumHeight){ maximumHeight = 65535 } - rowObject.height = Math.min(Math.max(minimumHeight, h + delta.y),maximumHeight) - control.setRow(row,rowObject) + rowModel.height = Math.min(Math.max(minimumHeight, h + delta.y),maximumHeight) + control.setRow(row,rowModel) table_view.forceLayout() } } @@ -790,6 +835,123 @@ Rectangle { } } } + Item{ + anchors{ + left: header_vertical.right + top: parent.top + bottom: parent.bottom + right: parent.right + } + Component{ + id: com_table_frozen + Rectangle{ + id: item_layout_frozen + anchors.fill: parent + color: { + if(Window.active){ + return FluTheme.dark ? Qt.rgba(48/255,48/255,48/255,1) :Qt.rgba(1,1,1,1) + } + return FluTheme.dark ? Qt.rgba(56/255,56/255,56/255,1) :Qt.rgba(243/255,243/255,243/255,1) + } + Rectangle{ + z:99 + anchors.fill: parent + border.color: FluTheme.dark ? Qt.rgba(26/255,26/255,26/255,0.6) : Qt.rgba(191/255,191/255,191/255,0.3) + FluShadow{ + radius: 0 + anchors.fill: parent + } + color: "#00000000" + } + TableView { + id:item_table_frozen_header + model: header_column_model + boundsBehavior: Flickable.StopAtBounds + clip: true + interactive: false + anchors{ + left: parent.left + right: parent.right + top: parent.top + bottom: item_table_frozen.top + } + delegate: com_column_header_delegate + } + TableView{ + property string dataIndex: columnModel.dataIndex + id: item_table_frozen + clip: true + interactive: false + anchors{ + fill: parent + topMargin: header_horizontal.height + } + boundsBehavior: TableView.StopAtBounds + model: table_sort_model + delegate: com_table_delegate + syncDirection: Qt.Vertical + syncView: table_view + Component.onCompleted: { + item_table_frozen_header.contentX = columnModel.width * _index + item_table_frozen.contentX = columnModel.width * _index + } + } + } + } + Repeater{ + model: control.columnSource + delegate: FluLoader{ + id: item_layout_frozen + readonly property int _index : model.index + readonly property var columnModel : control.columnSource[_index] + Connections{ + target: d + function onTableItemLayout(column){ + if(item_layout_frozen._index === column){ + updateLayout() + } + } + } + Connections{ + target: table_view + function onContentXChanged(){ + updateLayout() + } + } + function updateLayout(){ + width = table_view.columnWidthProvider(_index) + x = Qt.binding(function(){ + var minX = 0 + var maxX = table_view.width-item_layout_frozen.width + for(var i=0;i<_index;i++){ + var item = control.columnSource[i] + if(item.frozen){ + minX = minX + item.width + } + } + for(i=_index+1;i{ + var obj = control.getRow(row) + obj[control.columnSource[column].dataIndex] = text + control.setRow(row,obj) + } + z:999 + } Item{ anchors.fill: parent visible: item_table_mouse.isRowSelected @@ -408,71 +465,37 @@ Rectangle { table_view.flick(0,1) } delegate: com_table_delegate - 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)=>{ - var obj = control.getRow(row) - obj[control.columnSource[column].dataIndex] = text - control.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 - } } } Component{ id:com_column_header_delegate Rectangle{ - id:column_item_control + id: column_item_control + property var currentTableView : TableView.view readonly property real cellPadding: 8 property bool canceled: false property var _model: model - readonly property var columnModel : control.columnSource[column] - implicitWidth: { - return (item_column_loader.item && item_column_loader.item.implicitWidth) + (cellPadding * 2) + readonly property var columnModel : control.columnSource[_index] + readonly property int _index : { + const isDataIndex = (element) => { + return element.dataIndex === display.dataIndex + } + return control.columnSource.findIndex(isDataIndex) + } + readonly property bool isHeaderHorizontal: TableView.view == header_horizontal + implicitWidth: { + if(column_item_control.isHeaderHorizontal){ + return (item_column_loader.item && item_column_loader.item.implicitWidth) + (cellPadding * 2) + } + return TableView.view.width + } + implicitHeight: { + if(column_item_control.isHeaderHorizontal){ + return Math.max(36, (item_column_loader.item&&item_column_loader.item.implicitHeight) + (cellPadding * 2)) + } + return TableView.view.height } - 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 @@ -493,7 +516,7 @@ Rectangle { width: 1 height: parent.height anchors.left: parent.left - visible: column !== 0 + visible: column_item_control._index !== 0 color:"#00000000" } Rectangle{ @@ -502,7 +525,7 @@ Rectangle { height: parent.height anchors.right: parent.right color:"#00000000" - visible: column === table_view.columns - 1 + visible: column_item_control._index === table_view.columns - 1 } MouseArea{ id:column_item_control_mouse @@ -525,7 +548,7 @@ Rectangle { FluLoader{ id:item_column_loader property var model: column_item_control._model - property var display: model.display + property var display: model.display.title property var tableView: table_view property var sourceModel: control.sourceModel property bool isObject: typeof(display) == "object" @@ -535,7 +558,7 @@ Rectangle { } return {} } - property int column: model.column + property int column: column_item_control._index width: parent.width height: parent.height sourceComponent: { @@ -552,7 +575,7 @@ Rectangle { anchors.right: parent.right acceptedButtons: Qt.LeftButton hoverEnabled: true - visible: !(columnModel.width === columnModel.minimumWidth && columnModel.width === columnModel.maximumWidth && columnModel.width) + visible: !columnModel.frozen && !(columnModel.width === columnModel.minimumWidth && columnModel.width === columnModel.maximumWidth && columnModel.width) cursorShape: Qt.SplitHCursor preventStealing: true onPressed : @@ -587,6 +610,7 @@ Rectangle { columnModel.width = Math.min(Math.max(minimumWidth, w + delta.x),maximumWidth) table_view.forceLayout() header_horizontal.forceLayout() +// column_item_control.currentTableView.forceLayout() } } } @@ -811,60 +835,108 @@ Rectangle { } } } - Item{ anchors{ left: header_vertical.right top: parent.top bottom: parent.bottom + right: parent.right } - Component{ id: com_table_frozen - Item{ + Rectangle{ id: item_layout_frozen - property var sourceModel:FluTableModel { - columnSource: columnModel - rows: control.sourceModel.rows + anchors.fill: parent + color: { + if(Window.active){ + return FluTheme.dark ? Qt.rgba(48/255,48/255,48/255,1) :Qt.rgba(1,1,1,1) + } + return FluTheme.dark ? Qt.rgba(56/255,56/255,56/255,1) :Qt.rgba(243/255,243/255,243/255,1) + } + Rectangle{ + z:99 + anchors.fill: parent + border.color: FluTheme.dark ? Qt.rgba(26/255,26/255,26/255,0.6) : Qt.rgba(191/255,191/255,191/255,0.3) + FluShadow{ + radius: 0 + anchors.fill: parent + } + color: "#00000000" + } + TableView { + id:item_table_frozen_header + model: header_column_model + boundsBehavior: Flickable.StopAtBounds + clip: true + interactive: false + anchors{ + left: parent.left + right: parent.right + top: parent.top + bottom: item_table_frozen.top + } + delegate: com_column_header_delegate } TableView{ + property string dataIndex: columnModel.dataIndex + id: item_table_frozen clip: true + interactive: false anchors{ fill: parent topMargin: header_horizontal.height } - model: control.sourceModel + boundsBehavior: TableView.StopAtBounds + model: table_sort_model delegate: com_table_delegate syncDirection: Qt.Vertical syncView: table_view + Component.onCompleted: { + item_table_frozen_header.contentX = columnModel.width * _index + item_table_frozen.contentX = columnModel.width * _index + } } } } - Repeater{ model: control.columnSource delegate: FluLoader{ id: item_layout_frozen - readonly property var columnModel : control.columnSource[model.index] - readonly property int index : model.index - width: table_view.columnWidthProvider(index) + readonly property int _index : model.index + readonly property var columnModel : control.columnSource[_index] Connections{ target: d function onTableItemLayout(column){ - if(column === index){ - item_layout_frozen.updateLayout() + if(item_layout_frozen._index === column){ + updateLayout() } } } Connections{ target: table_view function onContentXChanged(){ - item_layout_frozen.updateLayout() + updateLayout() } } function updateLayout(){ - width = table_view.columnWidthProvider(index) - x = Qt.binding(function(){ return Math.min(Math.max(columnModel.x - table_view.contentX,0),table_view.width-item_layout_frozen.width) }) + width = table_view.columnWidthProvider(_index) + x = Qt.binding(function(){ + var minX = 0 + var maxX = table_view.width-item_layout_frozen.width + for(var i=0;i<_index;i++){ + var item = control.columnSource[i] + if(item.frozen){ + minX = minX + item.width + } + } + for(i=_index+1;i Date: Tue, 30 Apr 2024 13:06:27 +0800 Subject: [PATCH 08/35] update --- src/Qt5/imports/FluentUI/Controls/FluTableView.qml | 1 + src/Qt6/imports/FluentUI/Controls/FluTableView.qml | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml index 2e04312e..1796723e 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml @@ -853,6 +853,7 @@ Rectangle { } return FluTheme.dark ? Qt.rgba(56/255,56/255,56/255,1) :Qt.rgba(243/255,243/255,243/255,1) } + visible: table_view.rows !== 0 Rectangle{ z:99 anchors.fill: parent diff --git a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml index 850fc786..bd592d44 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml @@ -853,6 +853,7 @@ Rectangle { } return FluTheme.dark ? Qt.rgba(56/255,56/255,56/255,1) :Qt.rgba(243/255,243/255,243/255,1) } + visible: table_view.rows !== 0 Rectangle{ z:99 anchors.fill: parent From 91bda2a22c84c18bb9dc51f113e811b47e29ec8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Tue, 30 Apr 2024 13:07:46 +0800 Subject: [PATCH 09/35] update --- example/example_en_US.ts | 16 ++++++++-------- example/example_zh_CN.ts | 16 ++++++++-------- example/qml/page/T_TableView.qml | 5 ----- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/example/example_en_US.ts b/example/example_en_US.ts index 7ed2f842..81c845a7 100644 --- a/example/example_en_US.ts +++ b/example/example_en_US.ts @@ -1964,7 +1964,7 @@ Some contents... - + Name @@ -2014,37 +2014,37 @@ Some contents... - + Avatar - + Address - + Nickname - + Long String - + Options - + <Previous - + Next> diff --git a/example/example_zh_CN.ts b/example/example_zh_CN.ts index de111fa9..09f6aac2 100644 --- a/example/example_zh_CN.ts +++ b/example/example_zh_CN.ts @@ -2110,7 +2110,7 @@ Some contents... - + Name 名称 @@ -2150,37 +2150,37 @@ Some contents... 焦点未获取:请点击表格中的任意一项,作为插入的靶点! - + Avatar 头像 - + Address 地址 - + Nickname 昵称 - + Long String 长字符串 - + Options 操作 - + <Previous <上一页 - + Next> 下一页> diff --git a/example/qml/page/T_TableView.qml b/example/qml/page/T_TableView.qml index 962afb3a..a5a4f8f3 100644 --- a/example/qml/page/T_TableView.qml +++ b/example/qml/page/T_TableView.qml @@ -505,9 +505,6 @@ FluContentPage{ { title: table_view.customItem(com_column_checbox,{checked:true}), dataIndex: 'checkbox', - width:100, - minimumWidth:100, - maximumWidth:300, frozen: true }, { @@ -556,8 +553,6 @@ FluContentPage{ title: qsTr("Options"), dataIndex: 'action', width:160, - minimumWidth:160, - maximumWidth:160, frozen:true } ] From f922978338df7a9d43f9dc5dd3f7e9531a7859a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Tue, 30 Apr 2024 19:27:57 +0800 Subject: [PATCH 10/35] update --- example/example_en_US.ts | 28 ++++++++++++++-------------- example/example_zh_CN.ts | 28 ++++++++++++++-------------- example/qml/page/T_TableView.qml | 2 -- example/qml/window/MainWindow.qml | 2 +- src/FluFrameless.cpp | 15 +++++++++++---- 5 files changed, 40 insertions(+), 35 deletions(-) diff --git a/example/example_en_US.ts b/example/example_en_US.ts index 81c845a7..0545ca0e 100644 --- a/example/example_en_US.ts +++ b/example/example_en_US.ts @@ -1964,7 +1964,7 @@ Some contents... - + Name @@ -1984,67 +1984,67 @@ Some contents... - + Age - + Clear All - + Delete Selection - + Add a row of Data - + Insert a Row - + Focus not acquired: Please click any item in the form as the target for insertion! - + Avatar - + Address - + Nickname - + Long String - + Options - + <Previous - + Next> diff --git a/example/example_zh_CN.ts b/example/example_zh_CN.ts index 09f6aac2..4c10312f 100644 --- a/example/example_zh_CN.ts +++ b/example/example_zh_CN.ts @@ -2110,7 +2110,7 @@ Some contents... - + Name 名称 @@ -2130,67 +2130,67 @@ Some contents... 全选 - + Age 年龄 - + Clear All 清除所有 - + Insert a Row 插入一行 - + Focus not acquired: Please click any item in the form as the target for insertion! 焦点未获取:请点击表格中的任意一项,作为插入的靶点! - + Avatar 头像 - + Address 地址 - + Nickname 昵称 - + Long String 长字符串 - + Options 操作 - + <Previous <上一页 - + Next> 下一页> - + Delete Selection 删除选中 - + Add a row of Data 添加一行数据 diff --git a/example/qml/page/T_TableView.qml b/example/qml/page/T_TableView.qml index a5a4f8f3..9f696693 100644 --- a/example/qml/page/T_TableView.qml +++ b/example/qml/page/T_TableView.qml @@ -273,7 +273,6 @@ FluContentPage{ } Component.onCompleted: { currentIndex=["100","300","500","1000"].findIndex((element) => element === display) - console.debug(textBox) textBox.forceActiveFocus() textBox.selectAll() } @@ -291,7 +290,6 @@ FluContentPage{ anchors.fill: parent focus: true Component.onCompleted: { - console.debug("333333") var data = ["傲来国界花果山水帘洞","傲来国界坎源山脏水洞","大唐国界黑风山黑风洞","大唐国界黄风岭黄风洞","大唐国界骷髅山白骨洞","宝象国界碗子山波月洞","宝象国界平顶山莲花洞","宝象国界压龙山压龙洞","乌鸡国界号山枯松涧火云洞","乌鸡国界衡阳峪黑水河河神府"] var result = data.map(function(item) { return {title: item}; diff --git a/example/qml/window/MainWindow.qml b/example/qml/window/MainWindow.qml index 4fff0309..a34f1ffe 100644 --- a/example/qml/window/MainWindow.qml +++ b/example/qml/window/MainWindow.qml @@ -15,7 +15,7 @@ FluWindow { title: "FluentUI" width: 1000 height: 680 - minimumWidth: 1000 + minimumWidth: 800 minimumHeight: 200 launchMode: FluWindowType.SingleTask fitsAppBarWindows: true diff --git a/src/FluFrameless.cpp b/src/FluFrameless.cpp index 8fec5f5e..799b8627 100644 --- a/src/FluFrameless.cpp +++ b/src/FluFrameless.cpp @@ -87,7 +87,7 @@ void FluFrameless::componentComplete() { int w = window()->width(); int h = window()->height(); _current = window()->winId(); - window()->setFlags((window()->flags()) | Qt::CustomizeWindowHint | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint); + window()->setFlags((window()->flags()) | Qt::CustomizeWindowHint | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint | Qt::FramelessWindowHint); if (!_fixSize) { window()->setFlag(Qt::WindowMaximizeButtonHint); } @@ -106,14 +106,14 @@ void FluFrameless::componentComplete() { HWND hwnd = reinterpret_cast(window()->winId()); DWORD style = ::GetWindowLongPtr(hwnd, GWL_STYLE); if (_fixSize) { - ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_CAPTION); + ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_THICKFRAME | WS_CAPTION); for (int i = 0; i <= QGuiApplication::screens().count() - 1; ++i) { connect(QGuiApplication::screens().at(i), &QScreen::logicalDotsPerInchChanged, this, [=] { SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_FRAMECHANGED); }); } } else { - ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_CAPTION); + ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION); } SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); connect(window(), &QQuickWindow::screenChanged, this, [hwnd] { @@ -153,6 +153,7 @@ void FluFrameless::componentComplete() { const auto uMsg = msg->message; const auto wParam = msg->wParam; const auto lParam = msg->lParam; + static int offsetXY; if (uMsg == WM_WINDOWPOSCHANGING) { auto *wp = reinterpret_cast(lParam); if (wp != nullptr && (wp->flags & SWP_NOSIZE) == 0) { @@ -173,13 +174,19 @@ void FluFrameless::componentComplete() { *result = static_cast(hitTestResult); return true; } + if(clientRect->left - originalLeft != 0){ + offsetXY = clientRect->left - originalLeft; + } if (!isMaximum) { clientRect->top = originalTop; clientRect->bottom = originalBottom; clientRect->left = originalLeft; clientRect->right = originalRight; } else { - clientRect->top = 0; + clientRect->top = originalTop+offsetXY; + clientRect->bottom = originalBottom-offsetXY; + clientRect->left = originalLeft+offsetXY; + clientRect->right = originalRight-offsetXY; } _setMaximizeHovered(false); *result = WVR_REDRAW; From e471d5a230eeff0d26239b73ff92840a7eabe254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Tue, 30 Apr 2024 19:38:05 +0800 Subject: [PATCH 11/35] update --- src/FluFrameless.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/FluFrameless.cpp b/src/FluFrameless.cpp index 799b8627..1b6ce37b 100644 --- a/src/FluFrameless.cpp +++ b/src/FluFrameless.cpp @@ -7,6 +7,7 @@ #include "FluTools.h" #ifdef Q_OS_WIN + #pragma comment (lib, "user32.lib") #pragma comment (lib, "dwmapi.lib") @@ -14,7 +15,6 @@ #include #include - static inline QByteArray qtNativeEventType() { static const auto result = "windows_generic_MSG"; return result; @@ -48,7 +48,6 @@ static inline void setShadow(HWND hwnd) { } } - #endif bool containsCursorToItem(QQuickItem *item) { @@ -174,7 +173,7 @@ void FluFrameless::componentComplete() { *result = static_cast(hitTestResult); return true; } - if(clientRect->left - originalLeft != 0){ + if (clientRect->left - originalLeft != 0) { offsetXY = clientRect->left - originalLeft; } if (!isMaximum) { @@ -183,10 +182,10 @@ void FluFrameless::componentComplete() { clientRect->left = originalLeft; clientRect->right = originalRight; } else { - clientRect->top = originalTop+offsetXY; - clientRect->bottom = originalBottom-offsetXY; - clientRect->left = originalLeft+offsetXY; - clientRect->right = originalRight-offsetXY; + clientRect->top = originalTop + offsetXY; + clientRect->bottom = originalBottom - offsetXY; + clientRect->left = originalLeft + offsetXY; + clientRect->right = originalRight - offsetXY; } _setMaximizeHovered(false); *result = WVR_REDRAW; From 0b1755e9ebe78dd90d94205c8e721afd5cb60bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Wed, 1 May 2024 01:11:25 +0800 Subject: [PATCH 12/35] update --- src/FluFrameless.cpp | 5 ++++- src/Qt5/imports/FluentUI/Controls/FluWindow.qml | 4 ++-- src/Qt6/imports/FluentUI/Controls/FluWindow.qml | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/FluFrameless.cpp b/src/FluFrameless.cpp index 1b6ce37b..afe5d022 100644 --- a/src/FluFrameless.cpp +++ b/src/FluFrameless.cpp @@ -162,8 +162,8 @@ void FluFrameless::componentComplete() { } return false; } else if (uMsg == WM_NCCALCSIZE) { - const auto clientRect = ((wParam == FALSE) ? reinterpret_cast(lParam) : &(reinterpret_cast(lParam))->rgrc[0]); bool isMaximum = ::IsZoomed(hwnd); + const auto clientRect = ((wParam == FALSE) ? reinterpret_cast(lParam) : &(reinterpret_cast(lParam))->rgrc[0]); const LONG originalTop = clientRect->top; const LONG originalLeft = clientRect->left; const LONG originalBottom = clientRect->bottom; @@ -186,6 +186,9 @@ void FluFrameless::componentComplete() { clientRect->bottom = originalBottom - offsetXY; clientRect->left = originalLeft + offsetXY; clientRect->right = originalRight - offsetXY; +#if (QT_VERSION == QT_VERSION_CHECK(6, 5, 3) || QT_VERSION == QT_VERSION_CHECK(6, 6, 0)) + qWarning("This issue is Qt's own bug, which currently only exists in 6.5.3 and 6.6.0, and has been fixed in later versions"); +#endif } _setMaximizeHovered(false); *result = WVR_REDRAW; diff --git a/src/Qt5/imports/FluentUI/Controls/FluWindow.qml b/src/Qt5/imports/FluentUI/Controls/FluWindow.qml index 9457a11a..941bb1b1 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluWindow.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluWindow.qml @@ -61,8 +61,8 @@ Window { property var _windowRegister property string _route property bool _hideShadow: false - id:window - color:"transparent" + id: window + color: FluTools.isSoftware() ? window.backgroundColor : "transparent" Component.onCompleted: { FluRouter.addWindow(window) useSystemAppBar = FluApp.useSystemAppBar diff --git a/src/Qt6/imports/FluentUI/Controls/FluWindow.qml b/src/Qt6/imports/FluentUI/Controls/FluWindow.qml index b214e98c..0973a266 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluWindow.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluWindow.qml @@ -60,8 +60,8 @@ Window { property var _windowRegister property string _route property bool _hideShadow: false - id:window - color:"transparent" + id: window + color: FluTools.isSoftware() ? window.backgroundColor : "transparent" Component.onCompleted: { FluRouter.addWindow(window) useSystemAppBar = FluApp.useSystemAppBar From 8015dcc2f199a594379943141385656f0f7247a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Thu, 2 May 2024 23:52:30 +0800 Subject: [PATCH 13/35] update --- src/FluFrameless.cpp | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/FluFrameless.cpp b/src/FluFrameless.cpp index afe5d022..232c2afd 100644 --- a/src/FluFrameless.cpp +++ b/src/FluFrameless.cpp @@ -105,14 +105,22 @@ void FluFrameless::componentComplete() { HWND hwnd = reinterpret_cast(window()->winId()); DWORD style = ::GetWindowLongPtr(hwnd, GWL_STYLE); if (_fixSize) { +#if (QT_VERSION == QT_VERSION_CHECK(6, 5, 3) || QT_VERSION == QT_VERSION_CHECK(6, 6, 0)) + ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_THICKFRAME);; +#else ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_THICKFRAME | WS_CAPTION); +#endif for (int i = 0; i <= QGuiApplication::screens().count() - 1; ++i) { connect(QGuiApplication::screens().at(i), &QScreen::logicalDotsPerInchChanged, this, [=] { SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_FRAMECHANGED); }); } } else { +#if (QT_VERSION == QT_VERSION_CHECK(6, 5, 3) || QT_VERSION == QT_VERSION_CHECK(6, 6, 0)) + ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME); +#else ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION); +#endif } SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); connect(window(), &QQuickWindow::screenChanged, this, [hwnd] { @@ -162,7 +170,6 @@ void FluFrameless::componentComplete() { } return false; } else if (uMsg == WM_NCCALCSIZE) { - bool isMaximum = ::IsZoomed(hwnd); const auto clientRect = ((wParam == FALSE) ? reinterpret_cast(lParam) : &(reinterpret_cast(lParam))->rgrc[0]); const LONG originalTop = clientRect->top; const LONG originalLeft = clientRect->left; @@ -176,6 +183,13 @@ void FluFrameless::componentComplete() { if (clientRect->left - originalLeft != 0) { offsetXY = clientRect->left - originalLeft; } +#if (QT_VERSION == QT_VERSION_CHECK(6, 5, 3) || QT_VERSION == QT_VERSION_CHECK(6, 6, 0)) + clientRect->top = originalTop; + clientRect->bottom = originalBottom; + clientRect->left = originalLeft; + clientRect->right = originalRight; +#else + bool isMaximum = ::IsZoomed(hwnd); if (!isMaximum) { clientRect->top = originalTop; clientRect->bottom = originalBottom; @@ -186,10 +200,8 @@ void FluFrameless::componentComplete() { clientRect->bottom = originalBottom - offsetXY; clientRect->left = originalLeft + offsetXY; clientRect->right = originalRight - offsetXY; -#if (QT_VERSION == QT_VERSION_CHECK(6, 5, 3) || QT_VERSION == QT_VERSION_CHECK(6, 6, 0)) - qWarning("This issue is Qt's own bug, which currently only exists in 6.5.3 and 6.6.0, and has been fixed in later versions"); -#endif } +#endif _setMaximizeHovered(false); *result = WVR_REDRAW; return true; @@ -258,6 +270,17 @@ void FluFrameless::componentComplete() { } *result = TRUE; return true; + } else if (uMsg == WM_GETMINMAXINFO) { + auto *minmaxInfo = reinterpret_cast(lParam); + auto pixelRatio = window()->devicePixelRatio(); + auto geometry = window()->screen()->availableGeometry(); + RECT rect; + SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0); + minmaxInfo->ptMaxPosition.x = rect.left; + minmaxInfo->ptMaxPosition.y = rect.top; + minmaxInfo->ptMaxSize.x = qRound(geometry.width() * pixelRatio); + minmaxInfo->ptMaxSize.y = qRound(geometry.height() * pixelRatio); + return false; } else if (_isWindows11OrGreater && (uMsg == WM_NCLBUTTONDBLCLK || uMsg == WM_NCLBUTTONDOWN)) { if (_hitMaximizeButton()) { QMouseEvent event = QMouseEvent(QEvent::MouseButtonPress, QPoint(), QPoint(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); From 0eb4d9f346cfaa903b9970910e25773cbd3171ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Fri, 3 May 2024 01:54:38 +0800 Subject: [PATCH 14/35] update --- src/FluFrameless.cpp | 23 ++++++++++++++----- .../FluentUI/Controls/FluPagination.qml | 3 --- .../FluentUI/Controls/FluPagination.qml | 11 ++++----- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/FluFrameless.cpp b/src/FluFrameless.cpp index 232c2afd..4c2e92c8 100644 --- a/src/FluFrameless.cpp +++ b/src/FluFrameless.cpp @@ -54,9 +54,9 @@ bool containsCursorToItem(QQuickItem *item) { if (!item || !item->isVisible()) { return false; } - auto point = QCursor::pos(); - auto rect = QRectF(item->mapToGlobal(QPoint(0, 0)), item->size()); - if (point.x() > rect.x() && point.x() < (rect.x() + rect.width()) && point.y() > rect.y() && point.y() < (rect.y() + rect.height())) { + auto point = item->window()->mapFromGlobal(QCursor::pos()); + auto rect = QRectF(item->mapToItem(item->window()->contentItem(), QPointF(0, 0)), item->size()); + if (rect.contains(point)) { return true; } return false; @@ -297,7 +297,9 @@ void FluFrameless::componentComplete() { } } else if (uMsg == WM_NCRBUTTONDOWN) { if (wParam == HTCAPTION) { - _showSystemMenu(QCursor::pos()); + auto pos = window()->position(); + auto offset = window()->mapFromGlobal(QCursor::pos()); + _showSystemMenu(QPoint(pos.x() + offset.x(), pos.y() + offset.y())); } } else if (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN) { const bool altPressed = ((wParam == VK_MENU) || (::GetKeyState(VK_MENU) < 0)); @@ -335,6 +337,15 @@ bool FluFrameless::_isFullScreen() { void FluFrameless::_showSystemMenu(QPoint point) { #ifdef Q_OS_WIN + QScreen *screen = window()->screen(); + if (!screen) { + screen = QGuiApplication::primaryScreen(); + } + if (!screen) { + return; + } + const QPoint origin = screen->geometry().topLeft(); + auto nativePos = QPointF(QPointF(point - origin) * window()->devicePixelRatio()).toPoint() + origin; HWND hwnd = reinterpret_cast(window()->winId()); DWORD style = ::GetWindowLongPtr(hwnd, GWL_STYLE); ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_SYSMENU); @@ -353,8 +364,8 @@ void FluFrameless::_showSystemMenu(QPoint point) { ::EnableMenuItem(hMenu, SC_SIZE, MFS_DISABLED); ::EnableMenuItem(hMenu, SC_MAXIMIZE, MFS_DISABLED); } - const int result = ::TrackPopupMenu(hMenu, (TPM_RETURNCMD | (QGuiApplication::isRightToLeft() ? TPM_RIGHTALIGN : TPM_LEFTALIGN)), qRound(point.x() * window()->devicePixelRatio()), - qRound(point.y() * window()->devicePixelRatio()), 0, hwnd, nullptr); + const int result = ::TrackPopupMenu(hMenu, (TPM_RETURNCMD | (QGuiApplication::isRightToLeft() ? TPM_RIGHTALIGN : TPM_LEFTALIGN)), nativePos.x(), + nativePos.y(), 0, hwnd, nullptr); if (result != FALSE) { ::PostMessageW(hwnd, WM_SYSCOMMAND, result, 0); } diff --git a/src/Qt5/imports/FluentUI/Controls/FluPagination.qml b/src/Qt5/imports/FluentUI/Controls/FluPagination.qml index acf2d796..496410b1 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluPagination.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluPagination.qml @@ -38,7 +38,6 @@ Item { } Row { spacing: 5 - FluToggleButton { property int pageNumber: 1 visible: control.pageCount > 0 @@ -98,7 +97,6 @@ Item { sourceComponent: footer } } - function calcNewPage(page) { if (!page) return @@ -108,5 +106,4 @@ Item { control.pageCurrent = page_num control.requestPage(page_num, control.__itemPerPage) } - } diff --git a/src/Qt6/imports/FluentUI/Controls/FluPagination.qml b/src/Qt6/imports/FluentUI/Controls/FluPagination.qml index 91bff6e7..2c9550ce 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluPagination.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluPagination.qml @@ -1,7 +1,7 @@ -import QtQuick 2.15 -import QtQuick.Controls 2.15 -import QtQuick.Layouts 1.15 -import FluentUI 1.0 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import FluentUI Item { signal requestPage(int page, int count) @@ -37,7 +37,6 @@ Item { } Row { spacing: 5 - FluToggleButton { property int pageNumber: 1 visible: control.pageCount > 0 @@ -97,7 +96,6 @@ Item { sourceComponent: footer } } - function calcNewPage(page) { if (!page) return @@ -107,5 +105,4 @@ Item { control.pageCurrent = page_num control.requestPage(page_num, control.__itemPerPage) } - } From 18685b17ec5c1b7a08b9739a28d3d481227bae5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Fri, 3 May 2024 16:56:40 +0800 Subject: [PATCH 15/35] update --- src/FluFrameless.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FluFrameless.cpp b/src/FluFrameless.cpp index 4c2e92c8..da583654 100644 --- a/src/FluFrameless.cpp +++ b/src/FluFrameless.cpp @@ -169,8 +169,8 @@ void FluFrameless::componentComplete() { return true; } return false; - } else if (uMsg == WM_NCCALCSIZE) { - const auto clientRect = ((wParam == FALSE) ? reinterpret_cast(lParam) : &(reinterpret_cast(lParam))->rgrc[0]); + } else if (uMsg == WM_NCCALCSIZE && wParam == TRUE) { + const auto clientRect = &(reinterpret_cast(lParam))->rgrc[0]; const LONG originalTop = clientRect->top; const LONG originalLeft = clientRect->left; const LONG originalBottom = clientRect->bottom; From af270951da35874655f788ffabc10b97f493be67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Sat, 4 May 2024 23:56:20 +0800 Subject: [PATCH 16/35] update --- example/example_en_US.ts | 16 ++- example/example_zh_CN.ts | 16 ++- example/qml/page/T_RadioButton.qml | 107 ++++++++++++++++++ .../FluentUI/Controls/FluRadioButtons.qml | 82 +++++++++++--- .../FluentUI/Controls/FluRadioButtons.qml | 82 +++++++++++--- 5 files changed, 259 insertions(+), 44 deletions(-) diff --git a/example/example_en_US.ts b/example/example_en_US.ts index 0545ca0e..7b417a49 100644 --- a/example/example_en_US.ts +++ b/example/example_en_US.ts @@ -1700,22 +1700,30 @@ My only desire is to be permitted to drive out the traitors and restore the Han. - + + + Disabled - + + + Radio Button_1 - + + + Radio Button_2 - + + + Radio Button_3 diff --git a/example/example_zh_CN.ts b/example/example_zh_CN.ts index 4c10312f..e798dfc6 100644 --- a/example/example_zh_CN.ts +++ b/example/example_zh_CN.ts @@ -1808,22 +1808,30 @@ My only desire is to be permitted to drive out the traitors and restore the Han. - + + + Disabled 禁用 - + + + Radio Button_1 - + + + Radio Button_2 - + + + Radio Button_3 diff --git a/example/qml/page/T_RadioButton.qml b/example/qml/page/T_RadioButton.qml index 2f08e329..46fae27b 100644 --- a/example/qml/page/T_RadioButton.qml +++ b/example/qml/page/T_RadioButton.qml @@ -58,6 +58,7 @@ FluScrollablePage{ verticalCenter: parent.verticalCenter left: parent.left } + currentIndex: 1 FluRadioButton{ disabled: radio_button_switch2.checked text: qsTr("Radio Button_1") @@ -97,4 +98,110 @@ FluScrollablePage{ }' } + FluFrame{ + Layout.fillWidth: true + Layout.preferredHeight: 60 + padding: 10 + Layout.topMargin: 20 + FluRadioButtons{ + spacing: 8 + anchors.verticalCenter: parent.verticalCenter + anchors{ + verticalCenter: parent.verticalCenter + left: parent.left + } + orientation: Qt.Horizontal + currentIndex: 1 + FluRadioButton{ + disabled: radio_button_switch3.checked + text: qsTr("Radio Button_1") + } + FluRadioButton{ + disabled: radio_button_switch3.checked + text: qsTr("Radio Button_2") + } + FluRadioButton{ + disabled: radio_button_switch3.checked + text: qsTr("Radio Button_3") + } + } + FluToggleSwitch{ + id: radio_button_switch3 + anchors{ + right: parent.right + verticalCenter: parent.verticalCenter + } + text: qsTr("Disabled") + } + } + CodeExpander{ + Layout.fillWidth: true + Layout.topMargin: -6 + code:'FluRadioButtons{ + spacing: 8 + orientation: Qt.Horizontal + FluRadioButton{ + text:"Radio Button_1" + } + FluRadioButton{ + text:"Radio Button_2" + } + FluRadioButton{ + text:"Radio Button_3" + } +}' + } + + FluFrame{ + Layout.fillWidth: true + Layout.preferredHeight: 100 + padding: 10 + Layout.topMargin: 20 + FluRadioButtons{ + spacing: 8 + anchors.verticalCenter: parent.verticalCenter + anchors{ + verticalCenter: parent.verticalCenter + left: parent.left + } + currentIndex: 1 + FluCheckBox{ + disabled: radio_button_switch4.checked + text: qsTr("Radio Button_1") + } + FluCheckBox{ + disabled: radio_button_switch4.checked + text: qsTr("Radio Button_2") + } + FluCheckBox{ + disabled: radio_button_switch4.checked + text: qsTr("Radio Button_3") + } + } + FluToggleSwitch{ + id: radio_button_switch4 + anchors{ + right: parent.right + verticalCenter: parent.verticalCenter + } + text: qsTr("Disabled") + } + } + CodeExpander{ + Layout.fillWidth: true + Layout.topMargin: -6 + code:'FluRadioButtons{ + spacing: 8 + FluCheckBox{ + text:"Radio Button_1" + } + FluCheckBox{ + text:"Radio Button_2" + } + FluCheckBox{ + text:"Radio Button_3" + } +}' + } + } diff --git a/src/Qt5/imports/FluentUI/Controls/FluRadioButtons.qml b/src/Qt5/imports/FluentUI/Controls/FluRadioButtons.qml index 83060795..2c79e575 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluRadioButtons.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluRadioButtons.qml @@ -3,30 +3,76 @@ import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import FluentUI 1.0 -ColumnLayout { - default property alias buttons: control.data - property int currentIndex : -1 +Item{ id:control - onCurrentIndexChanged: { - for(var i = 0;i buttons + property int currentIndex : -1 + property int spacing: 8 + property int orientation: Qt.Vertical + QtObject{ + id: d + function updateChecked(){ + if(buttons.length === 0){ + return + } + for(var i = 0;i buttons + property int currentIndex : -1 + property int spacing: 8 + property int orientation: Qt.Vertical + QtObject{ + id: d + function updateChecked(){ + if(buttons.length === 0){ + return + } + for(var i = 0;i Date: Sun, 5 May 2024 00:06:01 +0800 Subject: [PATCH 17/35] update --- src/FluFrameless.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/FluFrameless.cpp b/src/FluFrameless.cpp index da583654..862842f4 100644 --- a/src/FluFrameless.cpp +++ b/src/FluFrameless.cpp @@ -271,6 +271,7 @@ void FluFrameless::componentComplete() { *result = TRUE; return true; } else if (uMsg == WM_GETMINMAXINFO) { +#if (QT_VERSION == QT_VERSION_CHECK(6, 5, 3) || QT_VERSION == QT_VERSION_CHECK(6, 6, 0)) auto *minmaxInfo = reinterpret_cast(lParam); auto pixelRatio = window()->devicePixelRatio(); auto geometry = window()->screen()->availableGeometry(); @@ -280,6 +281,7 @@ void FluFrameless::componentComplete() { minmaxInfo->ptMaxPosition.y = rect.top; minmaxInfo->ptMaxSize.x = qRound(geometry.width() * pixelRatio); minmaxInfo->ptMaxSize.y = qRound(geometry.height() * pixelRatio); +#endif return false; } else if (_isWindows11OrGreater && (uMsg == WM_NCLBUTTONDBLCLK || uMsg == WM_NCLBUTTONDOWN)) { if (_hitMaximizeButton()) { From 0d61e33ef17b0bec2b47e36dd4f176b5da512cd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Sun, 5 May 2024 15:52:46 +0800 Subject: [PATCH 18/35] update --- src/CMakeLists.txt | 10 ++ src/FluentUI.cpp | 6 ++ src/Qt5/imports/FluentUI/plugins.qmltypes | 113 ++++++++++++++++++---- 3 files changed, 112 insertions(+), 17 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 70040b5e..6e1b55e7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -180,5 +180,15 @@ target_link_libraries(${PROJECT_NAME} PUBLIC Qt${QT_VERSION_MAJOR}::Qml ) +if (${QT_VERSION_MAJOR} LESS_EQUAL 6) + find_program(QML_PLUGIN_DUMP NAMES qmlplugindump) + add_custom_target(Script-Generate-QmlTypes + COMMAND ${QML_PLUGIN_DUMP} -nonrelocatable FluentUI 1.0 ${CMAKE_CURRENT_BINARY_DIR} > ${CMAKE_CURRENT_SOURCE_DIR}/Qt5/imports/FluentUI/plugins.qmltypes + COMMENT "Generate qmltypes........." + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/Qt5/imports/FluentUI/plugins.qmltypes + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) +endif() + #安装 install(DIRECTORY ${FLUENTUI_QML_PLUGIN_DIRECTORY} DESTINATION ${CMAKE_INSTALL_PREFIX}/imports) diff --git a/src/FluentUI.cpp b/src/FluentUI.cpp index bf86380c..6ea903f0 100644 --- a/src/FluentUI.cpp +++ b/src/FluentUI.cpp @@ -148,6 +148,12 @@ void FluentUI::registerTypes(const char *uri) const { qmlRegisterUncreatableMetaObject(FluTimelineType::staticMetaObject, uri, major, minor, "FluTimelineType", "Access to enums & flags only"); qmlRegisterUncreatableMetaObject(FluSheetType::staticMetaObject, uri, major, minor, "FluSheetType", "Access to enums & flags only"); +// qmlRegisterSingletonInstance(uri, major, minor, "FluApp", FluApp::getInstance()); +// qmlRegisterSingletonInstance(uri, major, minor, "FluColors", FluColors::getInstance()); +// qmlRegisterSingletonInstance(uri, major, minor, "FluTheme", FluTheme::getInstance()); +// qmlRegisterSingletonInstance(uri, major, minor, "FluTools", FluTools::getInstance()); +// qmlRegisterSingletonInstance(uri, major, minor, "FluTextStyle", FluTextStyle::getInstance()); + qmlRegisterModule(uri, major, minor); #endif } diff --git a/src/Qt5/imports/FluentUI/plugins.qmltypes b/src/Qt5/imports/FluentUI/plugins.qmltypes index 92a7e970..b46cb55a 100644 --- a/src/Qt5/imports/FluentUI/plugins.qmltypes +++ b/src/Qt5/imports/FluentUI/plugins.qmltypes @@ -4,7 +4,7 @@ import QtQuick.tooling 1.2 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -nonrelocatable FluentUI 1.0 D:\QtProjects\build-FluentUI-Desktop_Qt_5_15_2_MSVC2019_64bit-Release\src' +// 'qmlplugindump -nonrelocatable FluentUI 1.0 D:/QtProjects/build-FluentUI-Desktop_Qt_5_15_2_MSVC2019_64bit-Release/src' Module { dependencies: ["QtQuick 2.0"] @@ -25,6 +25,8 @@ Module { name: "FluApp" prototype: "QObject" exports: ["FluentUI/FluApp 1.0"] + isCreatable: false + isSingleton: true exportMetaObjectRevisions: [0] Property { name: "useSystemAppBar"; type: "bool" } Property { name: "windowIcon"; type: "string" } @@ -39,11 +41,11 @@ Module { Parameter { name: "target"; type: "QObject"; isPointer: true } } Method { - name: "iconDatas" + name: "iconData" type: "QJsonArray" Parameter { name: "keyword"; type: "string" } } - Method { name: "iconDatas"; type: "QJsonArray" } + Method { name: "iconData"; type: "QJsonArray" } } Component { name: "FluCalendarViewType" @@ -78,6 +80,8 @@ Module { name: "FluColors" prototype: "QObject" exports: ["FluentUI/FluColors 1.0"] + isCreatable: false + isSingleton: true exportMetaObjectRevisions: [0] Property { name: "Transparent"; type: "QColor" } Property { name: "Black"; type: "QColor" } @@ -265,11 +269,15 @@ Module { } } Component { - name: "FluTableSortProxyModel" - prototype: "QSortFilterProxyModel" - exports: ["FluentUI/FluTableSortProxyModel 1.0"] + name: "FluTableModel" + prototype: "QAbstractTableModel" + exports: ["FluentUI/FluTableModel 1.0"] exportMetaObjectRevisions: [0] - Property { name: "model"; type: "QAbstractTableModel"; isPointer: true } + Property { name: "dataSourceSize"; type: "int" } + Property { name: "columnSource"; type: "QList" } + Property { name: "rows"; type: "QList" } + Property { name: "rowCount"; type: "int"; isReadonly: true } + Method { name: "clear" } Method { name: "getRow" type: "QVariant" @@ -278,6 +286,46 @@ Module { Method { name: "setRow" Parameter { name: "rowIndex"; type: "int" } + Parameter { name: "row"; type: "QVariant" } + } + Method { + name: "insertRow" + Parameter { name: "rowIndex"; type: "int" } + Parameter { name: "row"; type: "QVariant" } + } + Method { + name: "removeRow" + Parameter { name: "rowIndex"; type: "int" } + Parameter { name: "rows"; type: "int" } + } + Method { + name: "removeRow" + Parameter { name: "rowIndex"; type: "int" } + } + Method { + name: "appendRow" + Parameter { name: "row"; type: "QVariant" } + } + } + Component { + name: "FluTableSortProxyModel" + prototype: "QSortFilterProxyModel" + exports: ["FluentUI/FluTableSortProxyModel 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "model"; type: "QVariant" } + Method { + name: "getRow" + type: "QVariant" + Parameter { name: "rowIndex"; type: "int" } + } + Method { + name: "setRow" + Parameter { name: "rowIndex"; type: "int" } + Parameter { name: "val"; type: "QVariant" } + } + Method { + name: "insertRow" + Parameter { name: "rowIndex"; type: "int" } Parameter { name: "val"; type: "QVariant" } } Method { @@ -298,6 +346,8 @@ Module { name: "FluTextStyle" prototype: "QObject" exports: ["FluentUI/FluTextStyle 1.0"] + isCreatable: false + isSingleton: true exportMetaObjectRevisions: [0] Property { name: "family"; type: "string" } Property { name: "Caption"; type: "QFont" } @@ -312,6 +362,8 @@ Module { name: "FluTheme" prototype: "QObject" exports: ["FluentUI/FluTheme 1.0"] + isCreatable: false + isSingleton: true exportMetaObjectRevisions: [0] Property { name: "dark"; type: "bool"; isReadonly: true } Property { name: "accentColor"; type: "FluAccentColor"; isPointer: true } @@ -380,6 +432,8 @@ Module { name: "FluTools" prototype: "QObject" exports: ["FluentUI/FluTools 1.0"] + isCreatable: false + isSingleton: true exportMetaObjectRevisions: [0] Method { name: "qtMajor"; type: "int" } Method { name: "qtMinor"; type: "int" } @@ -499,7 +553,7 @@ Module { } Component { name: "FluTreeModel" - prototype: "QAbstractItemModel" + prototype: "QAbstractTableModel" exports: ["FluentUI/FluTreeModel 1.0"] exportMetaObjectRevisions: [0] Property { name: "dataSourceSize"; type: "int" } @@ -2316,6 +2370,7 @@ Module { Parameter { name: "selection"; type: "QItemSelection" } } } + Component { name: "QAbstractTableModel"; prototype: "QAbstractItemModel" } Component { name: "QSortFilterProxyModel" prototype: "QAbstractProxyModel" @@ -2472,7 +2527,7 @@ Module { } Property { name: "layoutMacosButtons" - type: "FluLoader_QMLTYPE_14" + type: "FluLoader_QMLTYPE_13" isReadonly: true isPointer: true } @@ -2493,6 +2548,7 @@ Module { Property { name: "items"; type: "QVariant" } Property { name: "emptyText"; type: "string" } Property { name: "autoSuggestBoxReplacement"; type: "int" } + Property { name: "textRole"; type: "string" } Property { name: "filter"; type: "QVariant" } Signal { name: "itemClicked" @@ -2695,6 +2751,7 @@ Module { Property { name: "normalColor"; type: "QColor" } Property { name: "hoverColor"; type: "QColor" } Property { name: "disableColor"; type: "QColor" } + Property { name: "textBox"; type: "QQuickTextField"; isReadonly: true; isPointer: true } Signal { name: "commit" Parameter { name: "text"; type: "string" } @@ -3162,15 +3219,15 @@ Module { defaultProperty: "data" Property { name: "logo"; type: "QUrl" } Property { name: "title"; type: "string" } - Property { name: "items"; type: "FluObject_QMLTYPE_172"; isPointer: true } - Property { name: "footerItems"; type: "FluObject_QMLTYPE_172"; isPointer: true } + Property { name: "items"; type: "FluObject_QMLTYPE_173"; isPointer: true } + Property { name: "footerItems"; type: "FluObject_QMLTYPE_173"; 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_33"; isPointer: true } - Property { name: "navItemExpanderRightMenu"; type: "FluMenu_QMLTYPE_33"; isPointer: true } + Property { name: "navItemRightMenu"; type: "FluMenu_QMLTYPE_49"; isPointer: true } + Property { name: "navItemExpanderRightMenu"; type: "FluMenu_QMLTYPE_49"; isPointer: true } Property { name: "navCompactWidth"; type: "int" } Property { name: "navTopMargin"; type: "int" } Property { name: "cellHeight"; type: "int" } @@ -3246,6 +3303,8 @@ Module { Property { name: "pageCount"; type: "int" } Property { name: "__itemPerPage"; type: "int" } Property { name: "__pageButtonHalf"; type: "int" } + Property { name: "header"; type: "QQmlComponent"; isPointer: true } + Property { name: "footer"; type: "QQmlComponent"; isPointer: true } Signal { name: "requestPage" Parameter { name: "page"; type: "int" } @@ -3470,14 +3529,16 @@ Module { Property { name: "textColor"; type: "QColor" } } Component { - prototype: "QQuickColumnLayout" + prototype: "QQuickItem" name: "FluentUI/FluRadioButtons 1.0" exports: ["FluentUI/FluRadioButtons 1.0"] exportMetaObjectRevisions: [0] isComposite: true defaultProperty: "buttons" - Property { name: "currentIndex"; type: "int" } Property { name: "buttons"; type: "QObject"; isList: true; isReadonly: true } + Property { name: "currentIndex"; type: "int" } + Property { name: "spacing"; type: "int" } + Property { name: "orientation"; type: "int" } } Component { prototype: "QQuickRangeSlider" @@ -3757,6 +3818,7 @@ Module { exportMetaObjectRevisions: [0] isComposite: true defaultProperty: "data" + Property { name: "sourceModel"; type: "QVariant" } Property { name: "columnSource"; type: "QVariant" } Property { name: "dataSource"; type: "QVariant" } Property { name: "borderColor"; type: "QColor" } @@ -3767,7 +3829,6 @@ Module { Property { name: "rows"; type: "int"; isReadonly: true } Property { name: "columns"; type: "int"; isReadonly: true } Property { name: "current"; type: "QVariant"; isReadonly: true } - Property { name: "sourceModel"; type: "QQmlTableModel"; isReadonly: true; isPointer: true } Method { name: "closeEditor"; type: "QVariant" } Method { name: "resetPosition"; type: "QVariant" } Method { @@ -3803,6 +3864,13 @@ Module { Parameter { name: "rowIndex"; type: "QVariant" } Parameter { name: "rows"; type: "QVariant" } } + Method { + name: "insertRow" + type: "QVariant" + Parameter { name: "rowIndex"; type: "QVariant" } + Parameter { name: "obj"; type: "QVariant" } + } + Method { name: "currentIndex"; type: "QVariant" } Method { name: "appendRow" type: "QVariant" @@ -4068,6 +4136,7 @@ Module { Property { name: "closeListener"; type: "QVariant" } Property { name: "_windowRegister"; type: "QVariant" } Property { name: "_route"; type: "string" } + Property { name: "_hideShadow"; type: "bool" } Property { name: "contentData"; type: "QObject"; isList: true; isReadonly: true } Signal { name: "initArgument" @@ -4116,6 +4185,8 @@ Module { Parameter { name: "data"; type: "QVariant" } } Method { name: "showMaximized"; type: "QVariant" } + Method { name: "showMinimized"; type: "QVariant" } + Method { name: "showNormal"; type: "QVariant" } Method { name: "showLoading" type: "QVariant" @@ -4136,7 +4207,12 @@ Module { isComposite: true defaultProperty: "contentData" Property { name: "contentDelegate"; type: "QQmlComponent"; isPointer: true } - Method { name: "showDialog"; type: "QVariant" } + Method { + name: "showDialog" + type: "QVariant" + Parameter { name: "offsetX"; type: "QVariant" } + Parameter { name: "offsetY"; type: "QVariant" } + } Property { name: "windowIcon"; type: "string" } Property { name: "launchMode"; type: "int" } Property { name: "argument"; type: "QVariant" } @@ -4162,6 +4238,7 @@ Module { Property { name: "closeListener"; type: "QVariant" } Property { name: "_windowRegister"; type: "QVariant" } Property { name: "_route"; type: "string" } + Property { name: "_hideShadow"; type: "bool" } Property { name: "contentData"; type: "QObject"; isList: true; isReadonly: true } Signal { name: "initArgument" @@ -4210,6 +4287,8 @@ Module { Parameter { name: "data"; type: "QVariant" } } Method { name: "showMaximized"; type: "QVariant" } + Method { name: "showMinimized"; type: "QVariant" } + Method { name: "showNormal"; type: "QVariant" } Method { name: "showLoading" type: "QVariant" From ac253a3de5a8537bf20d6ff9064db11231f0ef44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Sun, 5 May 2024 20:32:48 +0800 Subject: [PATCH 19/35] fix bug --- .../imports/FluentUI/Controls/FluWindow.qml | 19 ++++++++++++++----- .../imports/FluentUI/Controls/FluWindow.qml | 19 ++++++++++++++----- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/Qt5/imports/FluentUI/Controls/FluWindow.qml b/src/Qt5/imports/FluentUI/Controls/FluWindow.qml index 941bb1b1..2acf7ca5 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluWindow.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluWindow.qml @@ -122,12 +122,21 @@ Window { fillMode: Image.PreserveAspectCrop asynchronous: true Component.onCompleted: { - var geometry = FluTools.desktopAvailableGeometry(window) - width = geometry.width - height = geometry.height - sourceSize = Qt.size(width,height) + img_back.updateLayout() source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath) } + Connections{ + target: window + function onScreenChanged(){ + img_back.updateLayout() + } + } + function updateLayout(){ + var geometry = FluTools.desktopAvailableGeometry(window) + img_back.width = geometry.width + img_back.height = geometry.height + img_back.sourceSize = Qt.size(img_back.width,img_back.height) + } Connections{ target: FluTheme function onDesktopImagePathChanged(){ @@ -157,7 +166,7 @@ Window { blurRadius: 64 visible: window.active && FluTheme.blurBehindWindowEnabled tintColor: FluTheme.dark ? Qt.rgba(0, 0, 0, 1) : Qt.rgba(1, 1, 1, 1) - targetRect: Qt.rect(window.x,window.y,window.width,window.height) + targetRect: Qt.rect(window.x-window.screen.virtualX,window.y-window.screen.virtualY,window.width,window.height) } } } diff --git a/src/Qt6/imports/FluentUI/Controls/FluWindow.qml b/src/Qt6/imports/FluentUI/Controls/FluWindow.qml index 0973a266..0401b858 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluWindow.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluWindow.qml @@ -121,12 +121,21 @@ Window { fillMode: Image.PreserveAspectCrop asynchronous: true Component.onCompleted: { - var geometry = FluTools.desktopAvailableGeometry(window) - width = geometry.width - height = geometry.height - sourceSize = Qt.size(width,height) + img_back.updateLayout() source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath) } + Connections{ + target: window + function onScreenChanged(){ + img_back.updateLayout() + } + } + function updateLayout(){ + var geometry = FluTools.desktopAvailableGeometry(window) + img_back.width = geometry.width + img_back.height = geometry.height + img_back.sourceSize = Qt.size(img_back.width,img_back.height) + } Connections{ target: FluTheme function onDesktopImagePathChanged(){ @@ -156,7 +165,7 @@ Window { blurRadius: 64 visible: window.active && FluTheme.blurBehindWindowEnabled tintColor: FluTheme.dark ? Qt.rgba(0, 0, 0, 1) : Qt.rgba(1, 1, 1, 1) - targetRect: Qt.rect(window.x,window.y,window.width,window.height) + targetRect: Qt.rect(window.x-window.screen.virtualX,window.y-window.screen.virtualY,window.width,window.height) } } } From 394a42cb9464877174b164bc9490e7a74f646f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Sun, 5 May 2024 21:33:47 +0800 Subject: [PATCH 20/35] update --- src/CMakeLists.txt | 2 +- src/FluFrameless.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6e1b55e7..9ae5ad36 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -180,7 +180,7 @@ target_link_libraries(${PROJECT_NAME} PUBLIC Qt${QT_VERSION_MAJOR}::Qml ) -if (${QT_VERSION_MAJOR} LESS_EQUAL 6) +if ((${QT_VERSION_MAJOR} LESS_EQUAL 6) AND (CMAKE_BUILD_TYPE MATCHES "Release")) find_program(QML_PLUGIN_DUMP NAMES qmlplugindump) add_custom_target(Script-Generate-QmlTypes COMMAND ${QML_PLUGIN_DUMP} -nonrelocatable FluentUI 1.0 ${CMAKE_CURRENT_BINARY_DIR} > ${CMAKE_CURRENT_SOURCE_DIR}/Qt5/imports/FluentUI/plugins.qmltypes diff --git a/src/FluFrameless.cpp b/src/FluFrameless.cpp index 862842f4..e526f633 100644 --- a/src/FluFrameless.cpp +++ b/src/FluFrameless.cpp @@ -259,17 +259,27 @@ void FluFrameless::componentComplete() { *result = HTCLIENT; return true; } else if (uMsg == WM_NCPAINT) { +#if (QT_VERSION == QT_VERSION_CHECK(6, 5, 3) || QT_VERSION == QT_VERSION_CHECK(6, 6, 0)) + *result = FALSE; + return true; +#else if (isCompositionEnabled()) { return false; } *result = FALSE; return true; +#endif } else if (uMsg == WM_NCACTIVATE) { +#if (QT_VERSION == QT_VERSION_CHECK(6, 5, 3) || QT_VERSION == QT_VERSION_CHECK(6, 6, 0)) + *result = TRUE; + return true; +#else if (isCompositionEnabled()) { return false; } *result = TRUE; return true; +#endif } else if (uMsg == WM_GETMINMAXINFO) { #if (QT_VERSION == QT_VERSION_CHECK(6, 5, 3) || QT_VERSION == QT_VERSION_CHECK(6, 6, 0)) auto *minmaxInfo = reinterpret_cast(lParam); From fe2543ab4d6f1790ae9a69ce5da79600aa2b2cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Sun, 5 May 2024 22:03:41 +0800 Subject: [PATCH 21/35] fix bug --- .../FluentUI/Controls/FluTableView.qml | 5 ++++ .../imports/FluentUI/Controls/FluTreeView.qml | 3 +++ .../FluentUI/Controls/FluTableView.qml | 5 ++++ .../imports/FluentUI/Controls/FluTreeView.qml | 25 +++++++++++-------- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml index 1796723e..14d6868c 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml @@ -19,6 +19,7 @@ Rectangle { property bool verticalHeaderVisible: true property color selectedBorderColor: FluTheme.primaryColor property color selectedColor: FluTools.withOpacity(FluTheme.primaryColor,0.3) + property alias view: table_view id:control color: { if(Window.active){ @@ -47,6 +48,9 @@ Rectangle { header_column_model.rows = [headerRow] } } + Component.onDestruction: { + table_view.contentY = 0 + } QtObject{ id:d property var current @@ -896,6 +900,7 @@ Rectangle { item_table_frozen_header.contentX = columnModel.width * _index item_table_frozen.contentX = columnModel.width * _index } + } } } diff --git a/src/Qt5/imports/FluentUI/Controls/FluTreeView.qml b/src/Qt5/imports/FluentUI/Controls/FluTreeView.qml index df84c4d5..619bbb29 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTreeView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTreeView.qml @@ -45,6 +45,9 @@ Rectangle { id:tree_model columnSource: control.columnSource } + Component.onDestruction: { + table_view.contentY = 0 + } onDepthPaddingChanged: { table_view.forceLayout() } diff --git a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml index bd592d44..9f9b34a1 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml @@ -19,6 +19,7 @@ Rectangle { property bool verticalHeaderVisible: true property color selectedBorderColor: FluTheme.primaryColor property color selectedColor: FluTools.withOpacity(FluTheme.primaryColor,0.3) + property alias view: table_view id:control color: { if(Window.active){ @@ -47,6 +48,9 @@ Rectangle { header_column_model.rows = [headerRow] } } + Component.onDestruction: { + table_view.contentY = 0 + } QtObject{ id:d property var current @@ -896,6 +900,7 @@ Rectangle { item_table_frozen_header.contentX = columnModel.width * _index item_table_frozen.contentX = columnModel.width * _index } + } } } diff --git a/src/Qt6/imports/FluentUI/Controls/FluTreeView.qml b/src/Qt6/imports/FluentUI/Controls/FluTreeView.qml index 432f1b74..8dc1e95d 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluTreeView.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluTreeView.qml @@ -28,21 +28,26 @@ Rectangle { 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] + if(columnSource.length !== 0){ + 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 } + Component.onDestruction: { + table_view.contentY = 0 + } onDepthPaddingChanged: { table_view.forceLayout() } From 4cca680029611215b9c8a3913f3d4b015d5aa2c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Sun, 5 May 2024 22:05:18 +0800 Subject: [PATCH 22/35] update --- src/Qt5/imports/FluentUI/Controls/FluTreeView.qml | 1 + src/Qt6/imports/FluentUI/Controls/FluTreeView.qml | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Qt5/imports/FluentUI/Controls/FluTreeView.qml b/src/Qt5/imports/FluentUI/Controls/FluTreeView.qml index 619bbb29..df160026 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTreeView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTreeView.qml @@ -17,6 +17,7 @@ Rectangle { property color selectedBorderColor: FluTheme.primaryColor property color selectedColor: FluTools.withOpacity(FluTheme.primaryColor,0.3) readonly property alias current: d.current + property alias view: table_view id:control color: { if(Window.active){ diff --git a/src/Qt6/imports/FluentUI/Controls/FluTreeView.qml b/src/Qt6/imports/FluentUI/Controls/FluTreeView.qml index 8dc1e95d..2f5df3e6 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluTreeView.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluTreeView.qml @@ -17,6 +17,7 @@ Rectangle { property color selectedBorderColor: FluTheme.primaryColor property color selectedColor: FluTools.withOpacity(FluTheme.primaryColor,0.3) readonly property alias current: d.current + property alias view: table_view id:control color: { if(Window.active){ From b916221d9f9d077091c33b3b2ed437c280027489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Tue, 7 May 2024 21:47:40 +0800 Subject: [PATCH 23/35] update --- example/example_en_US.ts | 2 +- example/example_zh_CN.ts | 2 +- example/qml/chart/T_LineChart.qml | 20 +++++++++++++++++++- src/FluTableModel.h | 1 - 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/example/example_en_US.ts b/example/example_en_US.ts index 7b417a49..5e6b05f1 100644 --- a/example/example_en_US.ts +++ b/example/example_en_US.ts @@ -1402,7 +1402,7 @@ My only desire is to be permitted to drive out the traitors and restore the Han. T_LineChart - + Line Chart diff --git a/example/example_zh_CN.ts b/example/example_zh_CN.ts index e798dfc6..9699fa8f 100644 --- a/example/example_zh_CN.ts +++ b/example/example_zh_CN.ts @@ -1500,7 +1500,7 @@ My only desire is to be permitted to drive out the traitors and restore the Han. T_LineChart - + Line Chart 线型图 diff --git a/example/qml/chart/T_LineChart.qml b/example/qml/chart/T_LineChart.qml index 88e92b3e..45837660 100644 --- a/example/qml/chart/T_LineChart.qml +++ b/example/qml/chart/T_LineChart.qml @@ -7,7 +7,9 @@ import "../component" FluScrollablePage{ + id: root title: qsTr("Line Chart") + property var data : [] FluFrame{ Layout.preferredWidth: 500 @@ -15,13 +17,14 @@ FluScrollablePage{ padding: 10 Layout.topMargin: 20 FluChart{ + id: chart anchors.fill: parent chartType: 'line' chartData: { return { labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], datasets: [{ label: 'My First Dataset', - data: [65, 59, 80, 81, 56, 55, 40], + data: root.data, fill: false, borderColor: 'rgb(75, 192, 192)', tension: 0.1 @@ -41,5 +44,20 @@ FluScrollablePage{ } } } + Timer{ + id: timer + interval: 300 + repeat: true + onTriggered: { + root.data.push(Math.random()*100) + if(root.data.length>7){ + root.data.shift() + } + chart.animateToNewData() + } + } + Component.onCompleted: { + timer.restart() + } } } diff --git a/src/FluTableModel.h b/src/FluTableModel.h index e2cf8879..80e56e57 100644 --- a/src/FluTableModel.h +++ b/src/FluTableModel.h @@ -8,7 +8,6 @@ class FluTableModel : public QAbstractTableModel { Q_OBJECT -Q_PROPERTY_AUTO(int, dataSourceSize) Q_PROPERTY_AUTO(QList, columnSource) Q_PROPERTY_AUTO(QList, rows) Q_PROPERTY(int rowCount READ rowCount CONSTANT) From 655eff4f62ae7957b17e0008cb19a563444d91c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Tue, 7 May 2024 22:16:42 +0800 Subject: [PATCH 24/35] update --- example/qml/page/T_RadioButton.qml | 2 +- .../imports/FluentUI/Controls/FluRadioButtons.qml | 12 ++++-------- .../imports/FluentUI/Controls/FluRadioButtons.qml | 12 ++++-------- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/example/qml/page/T_RadioButton.qml b/example/qml/page/T_RadioButton.qml index 46fae27b..0dce688d 100644 --- a/example/qml/page/T_RadioButton.qml +++ b/example/qml/page/T_RadioButton.qml @@ -164,7 +164,7 @@ FluScrollablePage{ verticalCenter: parent.verticalCenter left: parent.left } - currentIndex: 1 + currentIndex: -1 FluCheckBox{ disabled: radio_button_switch4.checked text: qsTr("Radio Button_1") diff --git a/src/Qt5/imports/FluentUI/Controls/FluRadioButtons.qml b/src/Qt5/imports/FluentUI/Controls/FluRadioButtons.qml index 2c79e575..8229930d 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluRadioButtons.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluRadioButtons.qml @@ -18,7 +18,9 @@ Item{ for(var i = 0;i=0 && currentIndex=0 && currentIndex Date: Wed, 8 May 2024 18:58:22 +0800 Subject: [PATCH 25/35] update --- src/Qt5/imports/FluentUI/Controls/FluTableView.qml | 10 ++++++++++ src/Qt6/imports/FluentUI/Controls/FluTableView.qml | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml index 14d6868c..8782cb2a 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml @@ -955,6 +955,16 @@ Rectangle { return false } sourceComponent: visible ? com_table_frozen : undefined + onStatusChanged: { + if(status === Loader.Ready){ + sourceComponent = Qt.binding(function(){ + if(modelData.frozen){ + return com_table_frozen + } + return undefined + }) + } + } } } } diff --git a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml index 9f9b34a1..8144b8ca 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml @@ -955,6 +955,16 @@ Rectangle { return false } sourceComponent: visible ? com_table_frozen : undefined + onStatusChanged: { + if(status === Loader.Ready){ + sourceComponent = Qt.binding(function(){ + if(modelData.frozen){ + return com_table_frozen + } + return undefined + }) + } + } } } } From cd3abc01e945ddebfa50d9f774dbf642b5c1c3d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Fri, 10 May 2024 19:20:01 +0800 Subject: [PATCH 26/35] fix bug --- src/FluFrameless.cpp | 30 +++++++++---------- .../FluentUI/Controls/FluTableView.qml | 4 ++- .../FluentUI/Controls/FluTableView.qml | 4 ++- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/FluFrameless.cpp b/src/FluFrameless.cpp index e526f633..a27a4d89 100644 --- a/src/FluFrameless.cpp +++ b/src/FluFrameless.cpp @@ -160,7 +160,6 @@ void FluFrameless::componentComplete() { const auto uMsg = msg->message; const auto wParam = msg->wParam; const auto lParam = msg->lParam; - static int offsetXY; if (uMsg == WM_WINDOWPOSCHANGING) { auto *wp = reinterpret_cast(lParam); if (wp != nullptr && (wp->flags & SWP_NOSIZE) == 0) { @@ -180,9 +179,6 @@ void FluFrameless::componentComplete() { *result = static_cast(hitTestResult); return true; } - if (clientRect->left - originalLeft != 0) { - offsetXY = clientRect->left - originalLeft; - } #if (QT_VERSION == QT_VERSION_CHECK(6, 5, 3) || QT_VERSION == QT_VERSION_CHECK(6, 6, 0)) clientRect->top = originalTop; clientRect->bottom = originalBottom; @@ -190,16 +186,19 @@ void FluFrameless::componentComplete() { clientRect->right = originalRight; #else bool isMaximum = ::IsZoomed(hwnd); - if (!isMaximum) { + if (isMaximum) { + auto geometry = window()->screen()->geometry(); + auto offsetX = qAbs(geometry.left()-originalLeft); + auto offsetY = qAbs(geometry.top()-originalTop); + clientRect->top = originalTop + offsetY; + clientRect->bottom = originalBottom - offsetY; + clientRect->left = originalLeft + offsetX; + clientRect->right = originalRight - offsetX; + } else { clientRect->top = originalTop; clientRect->bottom = originalBottom; clientRect->left = originalLeft; clientRect->right = originalRight; - } else { - clientRect->top = originalTop + offsetXY; - clientRect->bottom = originalBottom - offsetXY; - clientRect->left = originalLeft + offsetXY; - clientRect->right = originalRight - offsetXY; } #endif _setMaximizeHovered(false); @@ -359,8 +358,6 @@ void FluFrameless::_showSystemMenu(QPoint point) { const QPoint origin = screen->geometry().topLeft(); auto nativePos = QPointF(QPointF(point - origin) * window()->devicePixelRatio()).toPoint() + origin; HWND hwnd = reinterpret_cast(window()->winId()); - DWORD style = ::GetWindowLongPtr(hwnd, GWL_STYLE); - ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_SYSMENU); auto hMenu = ::GetSystemMenu(hwnd, FALSE); if (_isMaximized() || _isFullScreen()) { ::EnableMenuItem(hMenu, SC_MOVE, MFS_DISABLED); @@ -381,7 +378,6 @@ void FluFrameless::_showSystemMenu(QPoint point) { if (result != FALSE) { ::PostMessageW(hwnd, WM_SYSCOMMAND, result, 0); } - ::SetWindowLongPtr(hwnd, GWL_STYLE, style & ~WS_SYSMENU); #endif } @@ -406,11 +402,15 @@ bool FluFrameless::_hitMaximizeButton() { } void FluFrameless::_setMaximizePressed(bool val) { - _maximizeButton->setProperty("down", val); + if(_maximizeButton){ + _maximizeButton->setProperty("down", val); + } } void FluFrameless::_setMaximizeHovered(bool val) { - _maximizeButton->setProperty("hover", val); + if(_maximizeButton){ + _maximizeButton->setProperty("hover", val); + } } void FluFrameless::_updateCursor(int edges) { diff --git a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml index 8782cb2a..5c559897 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml @@ -905,7 +905,9 @@ Rectangle { } } Repeater{ - model: control.columnSource + Component.onCompleted: { + model = control.columnSource + } delegate: FluLoader{ id: item_layout_frozen readonly property int _index : model.index diff --git a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml index 8144b8ca..0e8382d7 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml @@ -905,7 +905,9 @@ Rectangle { } } Repeater{ - model: control.columnSource + Component.onCompleted: { + model = control.columnSource + } delegate: FluLoader{ id: item_layout_frozen readonly property int _index : model.index From 4997b991db3238376121af5aa9e9cbe3a52c4616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Fri, 10 May 2024 19:23:30 +0800 Subject: [PATCH 27/35] update --- .../FluentUI/Controls/FluTableView.qml | 50 ++++++++++--------- .../FluentUI/Controls/FluTableView.qml | 50 ++++++++++--------- 2 files changed, 52 insertions(+), 48 deletions(-) diff --git a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml index 5c559897..b398df29 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml @@ -20,6 +20,30 @@ Rectangle { property color selectedBorderColor: FluTheme.primaryColor property color selectedColor: FluTools.withOpacity(FluTheme.primaryColor,0.3) property alias view: table_view + property var columnWidthProvider: function(column) { + var columnModel = control.columnSource[column] + var width = columnModel.width + if(width){ + return width + } + var minimumWidth = columnModel.minimumWidth + if(minimumWidth){ + return minimumWidth + } + return d.defaultItemWidth + } + property var rowHeightProvider: function(row) { + var rowModel = control.getRow(row) + var height = rowModel.height + if(height){ + return height + } + var minimumHeight = rowModel._minimumHeight + if(minimumHeight){ + return minimumHeight + } + return d.defaultItemHeight + } id:control color: { if(Window.active){ @@ -438,30 +462,8 @@ Rectangle { anchors.fill: parent ScrollBar.horizontal:scroll_bar_h ScrollBar.vertical:scroll_bar_v - columnWidthProvider: function(column) { - var columnModel = control.columnSource[column] - var width = columnModel.width - if(width){ - return width - } - var minimumWidth = columnModel.minimumWidth - if(minimumWidth){ - return minimumWidth - } - return d.defaultItemWidth - } - rowHeightProvider: function(row) { - var rowModel = control.getRow(row) - var height = rowModel.height - if(height){ - return height - } - var minimumHeight = rowModel._minimumHeight - if(minimumHeight){ - return minimumHeight - } - return d.defaultItemHeight - } + columnWidthProvider: control.columnWidthProvider + rowHeightProvider: control.rowHeightProvider model: table_sort_model clip: true onRowsChanged: { diff --git a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml index 0e8382d7..4d3e2e53 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml @@ -20,6 +20,30 @@ Rectangle { property color selectedBorderColor: FluTheme.primaryColor property color selectedColor: FluTools.withOpacity(FluTheme.primaryColor,0.3) property alias view: table_view + property var columnWidthProvider: function(column) { + var columnModel = control.columnSource[column] + var width = columnModel.width + if(width){ + return width + } + var minimumWidth = columnModel.minimumWidth + if(minimumWidth){ + return minimumWidth + } + return d.defaultItemWidth + } + property var rowHeightProvider: function(row) { + var rowModel = control.getRow(row) + var height = rowModel.height + if(height){ + return height + } + var minimumHeight = rowModel._minimumHeight + if(minimumHeight){ + return minimumHeight + } + return d.defaultItemHeight + } id:control color: { if(Window.active){ @@ -438,30 +462,8 @@ Rectangle { anchors.fill: parent ScrollBar.horizontal:scroll_bar_h ScrollBar.vertical:scroll_bar_v - columnWidthProvider: function(column) { - var columnModel = control.columnSource[column] - var width = columnModel.width - if(width){ - return width - } - var minimumWidth = columnModel.minimumWidth - if(minimumWidth){ - return minimumWidth - } - return d.defaultItemWidth - } - rowHeightProvider: function(row) { - var rowModel = control.getRow(row) - var height = rowModel.height - if(height){ - return height - } - var minimumHeight = rowModel._minimumHeight - if(minimumHeight){ - return minimumHeight - } - return d.defaultItemHeight - } + columnWidthProvider: control.columnWidthProvider + rowHeightProvider: control.rowHeightProvider model: table_sort_model clip: true onRowsChanged: { From 6b941697b0abce76a24479580fe10fb5c7a89621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Sat, 11 May 2024 21:45:45 +0800 Subject: [PATCH 28/35] update --- src/FluFrameless.cpp | 2 +- src/FluTableModel.cpp | 10 -- src/FluTableModel.h | 4 - src/FluTreeModel.cpp | 10 -- src/FluTreeModel.h | 4 - .../FluentUI/Controls/FluTableView.qml | 97 +++++++++++-------- .../FluentUI/Controls/FluColorPicker.qml | 1 + .../FluentUI/Controls/FluTableView.qml | 97 +++++++++++-------- 8 files changed, 116 insertions(+), 109 deletions(-) diff --git a/src/FluFrameless.cpp b/src/FluFrameless.cpp index a27a4d89..0cd986f4 100644 --- a/src/FluFrameless.cpp +++ b/src/FluFrameless.cpp @@ -375,7 +375,7 @@ void FluFrameless::_showSystemMenu(QPoint point) { } const int result = ::TrackPopupMenu(hMenu, (TPM_RETURNCMD | (QGuiApplication::isRightToLeft() ? TPM_RIGHTALIGN : TPM_LEFTALIGN)), nativePos.x(), nativePos.y(), 0, hwnd, nullptr); - if (result != FALSE) { + if (result) { ::PostMessageW(hwnd, WM_SYSCOMMAND, result, 0); } #endif diff --git a/src/FluTableModel.cpp b/src/FluTableModel.cpp index 1a0e9c7c..eb22de64 100644 --- a/src/FluTableModel.cpp +++ b/src/FluTableModel.cpp @@ -31,16 +31,6 @@ QHash FluTableModel::roleNames() const { }; } -QModelIndex FluTableModel::parent(const QModelIndex &child) const { - return {}; -} - -QModelIndex FluTableModel::index(int row, int column, const QModelIndex &parent) const { - if (!hasIndex(row, column, parent) || parent.isValid()) - return {}; - return createIndex(row, column); -} - void FluTableModel::clear() { beginResetModel(); this->_rows.clear(); diff --git a/src/FluTableModel.h b/src/FluTableModel.h index 80e56e57..a734aa80 100644 --- a/src/FluTableModel.h +++ b/src/FluTableModel.h @@ -28,10 +28,6 @@ public: [[nodiscard]] QHash roleNames() const override; - [[nodiscard]] QModelIndex parent(const QModelIndex &child) const override; - - [[nodiscard]] QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override; - Q_INVOKABLE void clear(); Q_INVOKABLE QVariant getRow(int rowIndex); diff --git a/src/FluTreeModel.cpp b/src/FluTreeModel.cpp index baaaf010..f080a034 100644 --- a/src/FluTreeModel.cpp +++ b/src/FluTreeModel.cpp @@ -9,16 +9,6 @@ FluTreeModel::FluTreeModel(QObject *parent) : QAbstractTableModel{parent} { _dataSourceSize = 0; } -QModelIndex FluTreeModel::parent(const QModelIndex &child) const { - return {}; -} - -QModelIndex FluTreeModel::index(int row, int column, const QModelIndex &parent) const { - if (!hasIndex(row, column, parent) || parent.isValid()) - return {}; - return createIndex(row, column); -} - int FluTreeModel::rowCount(const QModelIndex &parent) const { return _rows.count(); } diff --git a/src/FluTreeModel.h b/src/FluTreeModel.h index 0fc1b53c..e9d5f48b 100644 --- a/src/FluTreeModel.h +++ b/src/FluTreeModel.h @@ -107,10 +107,6 @@ public: [[nodiscard]] QHash roleNames() const override; - [[nodiscard]] QModelIndex parent(const QModelIndex &child) const override; - - [[nodiscard]] QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override; - Q_INVOKABLE void removeRows(int row, int count); Q_INVOKABLE void insertRows(int row, const QList &data); diff --git a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml index b398df29..0f8da39d 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml @@ -225,22 +225,21 @@ Rectangle { id:com_table_delegate MouseArea{ id:item_table_mouse - implicitWidth: TableView.view.width property var _model: model property bool isMainTable: TableView.view == table_view property var currentTableView: TableView.view - visible: { + property bool isHide: { if(isMainTable && columnModel.frozen){ - return false + return true } if(!isMainTable){ if(currentTableView.dataIndex !== columnModel.dataIndex) - return false + return true } - return true + return false } property bool isRowSelected: { - if(rowModel === null) + if(!rowModel) return false if(d.current){ return rowModel._key === d.current._key @@ -248,11 +247,15 @@ Rectangle { return false } property bool editVisible: { + if(!rowModel) + return false if(d.editPosition && d.editPosition._key === rowModel._key && d.editPosition.column === column){ return true } return false } + implicitWidth: isHide ? Number.MIN_VALUE : TableView.view.width + visible: !isHide TableView.onPooled: { if(d.editPosition && d.editPosition.row === row && d.editPosition.column === column){ control.closeEditor() @@ -441,6 +444,11 @@ Rectangle { } } } + + onWidthChanged:{ + table_view.forceLayout() + } + MouseArea{ id:layout_mouse_table hoverEnabled: true @@ -490,7 +498,21 @@ Rectangle { return control.columnSource.findIndex(isDataIndex) } readonly property bool isHeaderHorizontal: TableView.view == header_horizontal + readonly property bool isHide: { + if(isHeaderHorizontal && columnModel.frozen){ + return true + } + if(!isHeaderHorizontal){ + if(currentTableView.dataIndex !== columnModel.dataIndex) + return true + } + return false + } + visible: !isHide implicitWidth: { + if(isHide){ + return Number.MIN_VALUE + } if(column_item_control.isHeaderHorizontal){ return (item_column_loader.item && item_column_loader.item.implicitWidth) + (cellPadding * 2) } @@ -616,7 +638,6 @@ Rectangle { columnModel.width = Math.min(Math.max(minimumWidth, w + delta.x),maximumWidth) table_view.forceLayout() header_horizontal.forceLayout() -// column_item_control.currentTableView.forceLayout() } } } @@ -870,12 +891,32 @@ Rectangle { } color: "#00000000" } + TableView{ + property string dataIndex: columnModel.dataIndex + id: item_table_frozen + interactive: false + clip: true + anchors{ + left: parent.left + right: parent.right + } + contentWidth: width + height: table_view.height + y: header_horizontal.height + boundsBehavior: TableView.StopAtBounds + model: table_view.model + delegate: table_view.delegate + syncDirection: Qt.Vertical + syncView: table_view + } TableView { + property string dataIndex: columnModel.dataIndex id:item_table_frozen_header model: header_column_model boundsBehavior: Flickable.StopAtBounds - clip: true interactive: false + clip: true + contentWidth: width anchors{ left: parent.left right: parent.right @@ -884,25 +925,11 @@ Rectangle { } delegate: com_column_header_delegate } - TableView{ - property string dataIndex: columnModel.dataIndex - id: item_table_frozen - clip: true - interactive: false - anchors{ - fill: parent - topMargin: header_horizontal.height + Connections{ + target: control + function onWidthChanged() { + item_table_frozen_header.forceLayout() } - boundsBehavior: TableView.StopAtBounds - model: table_sort_model - delegate: com_table_delegate - syncDirection: Qt.Vertical - syncView: table_view - Component.onCompleted: { - item_table_frozen_header.contentX = columnModel.width * _index - item_table_frozen.contentX = columnModel.width * _index - } - } } } @@ -932,17 +959,17 @@ Rectangle { width = table_view.columnWidthProvider(_index) x = Qt.binding(function(){ var minX = 0 - var maxX = table_view.width-item_layout_frozen.width + var maxX = table_view.width-width for(var i=0;i<_index;i++){ var item = control.columnSource[i] if(item.frozen){ - minX = minX + item.width + minX = minX + table_view.columnWidthProvider(i) } } for(i=_index+1;i Date: Sat, 11 May 2024 22:21:11 +0800 Subject: [PATCH 29/35] update --- .../FluentUI/Controls/FluTableView.qml | 29 +++++++++++-------- .../FluentUI/Controls/FluTableView.qml | 29 +++++++++++-------- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml index 0f8da39d..7843fc6f 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml @@ -446,7 +446,7 @@ Rectangle { } onWidthChanged:{ - table_view.forceLayout() + table_view.forceLayout() } MouseArea{ @@ -483,7 +483,7 @@ Rectangle { } Component{ - id:com_column_header_delegate + id: com_column_header_delegate Rectangle{ id: column_item_control property var currentTableView : TableView.view @@ -516,13 +516,13 @@ Rectangle { if(column_item_control.isHeaderHorizontal){ return (item_column_loader.item && item_column_loader.item.implicitWidth) + (cellPadding * 2) } - return TableView.view.width + return Math.max(TableView.view.width,Number.MIN_VALUE) } implicitHeight: { if(column_item_control.isHeaderHorizontal){ return Math.max(36, (item_column_loader.item&&item_column_loader.item.implicitHeight) + (cellPadding * 2)) } - return TableView.view.height + return Math.max(TableView.view.height,Number.MIN_VALUE) } color: FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(247/255,247/255,247/255,1) Rectangle{ @@ -924,12 +924,16 @@ Rectangle { bottom: item_table_frozen.top } delegate: com_column_header_delegate + Component.onCompleted: { + item_table_frozen_header.forceLayout() + } } Connections{ - target: control + target: table_view function onWidthChanged() { item_table_frozen_header.forceLayout() } + } } } @@ -941,6 +945,12 @@ Rectangle { id: item_layout_frozen readonly property int _index : model.index readonly property var columnModel : control.columnSource[_index] + readonly property bool isHide:{ + if(columnModel.frozen){ + return false + } + return true + } Connections{ target: d function onTableItemLayout(column){ @@ -979,13 +989,8 @@ Rectangle { updateLayout() } height: control.height - visible: { - if(item_layout_frozen.columnModel.frozen){ - return true - } - return false - } - sourceComponent: visible ? com_table_frozen : undefined + visible: !item_layout_frozen.isHide + sourceComponent: item_layout_frozen.isHide ? undefined : com_table_frozen } } } diff --git a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml index 5c31e27d..01ae9f0a 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml @@ -446,7 +446,7 @@ Rectangle { } onWidthChanged:{ - table_view.forceLayout() + table_view.forceLayout() } MouseArea{ @@ -483,7 +483,7 @@ Rectangle { } Component{ - id:com_column_header_delegate + id: com_column_header_delegate Rectangle{ id: column_item_control property var currentTableView : TableView.view @@ -516,13 +516,13 @@ Rectangle { if(column_item_control.isHeaderHorizontal){ return (item_column_loader.item && item_column_loader.item.implicitWidth) + (cellPadding * 2) } - return TableView.view.width + return Math.max(TableView.view.width,Number.MIN_VALUE) } implicitHeight: { if(column_item_control.isHeaderHorizontal){ return Math.max(36, (item_column_loader.item&&item_column_loader.item.implicitHeight) + (cellPadding * 2)) } - return TableView.view.height + return Math.max(TableView.view.height,Number.MIN_VALUE) } color: FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(247/255,247/255,247/255,1) Rectangle{ @@ -924,12 +924,16 @@ Rectangle { bottom: item_table_frozen.top } delegate: com_column_header_delegate + Component.onCompleted: { + item_table_frozen_header.forceLayout() + } } Connections{ - target: control + target: table_view function onWidthChanged() { item_table_frozen_header.forceLayout() } + } } } @@ -941,6 +945,12 @@ Rectangle { id: item_layout_frozen readonly property int _index : model.index readonly property var columnModel : control.columnSource[_index] + readonly property bool isHide:{ + if(columnModel.frozen){ + return false + } + return true + } Connections{ target: d function onTableItemLayout(column){ @@ -979,13 +989,8 @@ Rectangle { updateLayout() } height: control.height - visible: { - if(item_layout_frozen.columnModel.frozen){ - return true - } - return false - } - sourceComponent: visible ? com_table_frozen : undefined + visible: !item_layout_frozen.isHide + sourceComponent: item_layout_frozen.isHide ? undefined : com_table_frozen } } } From 876b2301418476a81e4a91b14f4c4066f57da230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Mon, 13 May 2024 18:28:28 +0800 Subject: [PATCH 30/35] update --- example/qml/window/MainWindow.qml | 6 +++--- example/src/component/OpenGLItem.cpp | 4 +++- src/CMakeLists.txt | 15 +++++++-------- src/FluFrameless.cpp | 6 +++++- .../imports/FluentUI/Controls/FluTableView.qml | 4 ++-- .../imports/FluentUI/Controls/FluTableView.qml | 4 ++-- 6 files changed, 22 insertions(+), 17 deletions(-) diff --git a/example/qml/window/MainWindow.qml b/example/qml/window/MainWindow.qml index a34f1ffe..9e2fa681 100644 --- a/example/qml/window/MainWindow.qml +++ b/example/qml/window/MainWindow.qml @@ -14,9 +14,9 @@ FluWindow { id:window title: "FluentUI" width: 1000 - height: 680 - minimumWidth: 800 - minimumHeight: 200 + height: 668 + minimumWidth: 668 + minimumHeight: 320 launchMode: FluWindowType.SingleTask fitsAppBarWindows: true appBar: FluAppBar { diff --git a/example/src/component/OpenGLItem.cpp b/example/src/component/OpenGLItem.cpp index 58e86d78..4e0f687a 100644 --- a/example/src/component/OpenGLItem.cpp +++ b/example/src/component/OpenGLItem.cpp @@ -2,6 +2,7 @@ #include #include +#include class FBORenderer : public QQuickFramebufferObject::Renderer, protected QOpenGLFunctions { public: @@ -47,6 +48,7 @@ QOpenGLFramebufferObject *FBORenderer::createFramebufferObject(const QSize &size } void FBORenderer::render() { + auto pixelRatio = item->window()->devicePixelRatio(); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glEnable(GL_DEPTH_TEST); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -61,7 +63,7 @@ void FBORenderer::render() { glBindBuffer(GL_ARRAY_BUFFER, 0); program.setAttributeArray(0, GL_FLOAT, values, 2); program.setUniformValue("t", (float) item->t()); - glViewport(0, 0, qRound(item->width()), qRound(item->height())); + glViewport(0, 0, qRound(item->width()*pixelRatio), qRound(item->height()*pixelRatio)); glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9ae5ad36..c4c40357 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,6 +11,13 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/.cmake/) #配置通用编译 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) +if (QT_VERSION VERSION_GREATER_EQUAL "6.3") + qt_standard_project_setup() +else () + set(CMAKE_AUTOMOC ON) + set(CMAKE_AUTORCC ON) + set(CMAKE_INCLUDE_CURRENT_DIR ON) +endif () #设置版本号 add_definitions(-DFLUENTUI_VERSION=1,7,5,0) @@ -35,14 +42,6 @@ if (NOT FLUENTUI_QML_PLUGIN_DIRECTORY) set(FLUENTUI_QML_PLUGIN_DIRECTORY ${QT_SDK_DIR}/qml/FluentUI) endif () -if (QT_VERSION VERSION_GREATER_EQUAL "6.3") - qt_standard_project_setup() -else () - set(CMAKE_AUTOMOC ON) - set(CMAKE_AUTORCC ON) - set(CMAKE_INCLUDE_CURRENT_DIR ON) -endif () - #国际化 find_program(QT_LUPDATE NAMES lupdate) find_program(QT_LRELEASE NAMES lrelease) diff --git a/src/FluFrameless.cpp b/src/FluFrameless.cpp index 0cd986f4..64d07563 100644 --- a/src/FluFrameless.cpp +++ b/src/FluFrameless.cpp @@ -131,10 +131,14 @@ void FluFrameless::componentComplete() { setShadow(hwnd); } #endif - h = qRound(h + _appbar->height()); + auto appBarHeight = _appbar->height(); + h = qRound(h + appBarHeight); if (_fixSize) { window()->setMaximumSize(QSize(w, h)); window()->setMinimumSize(QSize(w, h)); + }else{ + window()->setMinimumHeight(window()->minimumHeight()+appBarHeight); + window()->setMaximumHeight(window()->maximumHeight()+appBarHeight); } window()->resize(QSize(w, h)); connect(this, &FluFrameless::topmostChanged, this, [this] { diff --git a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml index 7843fc6f..681200aa 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTableView.qml @@ -499,8 +499,8 @@ Rectangle { } readonly property bool isHeaderHorizontal: TableView.view == header_horizontal readonly property bool isHide: { - if(isHeaderHorizontal && columnModel.frozen){ - return true + if(isHeaderHorizontal){ + return false } if(!isHeaderHorizontal){ if(currentTableView.dataIndex !== columnModel.dataIndex) diff --git a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml index 01ae9f0a..7efea849 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluTableView.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluTableView.qml @@ -499,8 +499,8 @@ Rectangle { } readonly property bool isHeaderHorizontal: TableView.view == header_horizontal readonly property bool isHide: { - if(isHeaderHorizontal && columnModel.frozen){ - return true + if(isHeaderHorizontal){ + return false } if(!isHeaderHorizontal){ if(currentTableView.dataIndex !== columnModel.dataIndex) From 354f7f2e3e72a8f1092598dedc498cf9dd947ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Thu, 16 May 2024 00:35:04 +0800 Subject: [PATCH 31/35] add hotkey --- THIRD_PARTY_COPYRIGHT.txt | 32 ++ example/example.qrc | 1 + example/example_en_US.ts | 56 +++ example/example_zh_CN.ts | 60 +++ example/qml/App.qml | 72 +++- example/qml/page/T_ShortcutPicker.qml | 20 +- example/qml/window/HotkeyWindow.qml | 26 ++ src/CMakeLists.txt | 19 + src/FluApp.cpp | 5 +- src/FluApp.h | 3 +- src/FluHotkey.cpp | 32 ++ src/FluHotkey.h | 24 ++ src/FluentUI.cpp | 2 + src/Qt5/imports/FluentUI/Controls/FluMenu.qml | 2 +- .../FluentUI/Controls/FluShortcutPicker.qml | 130 ++++-- src/Qt5/imports/FluentUI/plugins.qmltypes | 52 ++- src/Qt6/imports/FluentUI/Controls/FluMenu.qml | 2 +- .../FluentUI/Controls/FluShortcutPicker.qml | 130 ++++-- src/fluentui_en_US.ts | 46 ++- src/fluentui_zh_CN.ts | 46 ++- src/qhotkey/qhotkey.cpp | 377 ++++++++++++++++++ src/qhotkey/qhotkey.h | 130 ++++++ src/qhotkey/qhotkey_mac.cpp | 291 ++++++++++++++ src/qhotkey/qhotkey_p.h | 62 +++ src/qhotkey/qhotkey_win.cpp | 309 ++++++++++++++ src/qhotkey/qhotkey_x11.cpp | 268 +++++++++++++ 26 files changed, 2083 insertions(+), 114 deletions(-) create mode 100644 example/qml/window/HotkeyWindow.qml create mode 100644 src/FluHotkey.cpp create mode 100644 src/FluHotkey.h create mode 100644 src/qhotkey/qhotkey.cpp create mode 100644 src/qhotkey/qhotkey.h create mode 100644 src/qhotkey/qhotkey_mac.cpp create mode 100644 src/qhotkey/qhotkey_p.h create mode 100644 src/qhotkey/qhotkey_win.cpp create mode 100644 src/qhotkey/qhotkey_x11.cpp diff --git a/THIRD_PARTY_COPYRIGHT.txt b/THIRD_PARTY_COPYRIGHT.txt index 75557839..c657a604 100644 --- a/THIRD_PARTY_COPYRIGHT.txt +++ b/THIRD_PARTY_COPYRIGHT.txt @@ -34,6 +34,38 @@ providing powerful tools and support for this project. For more information about the Qt project, please visit the official Qt website (https://www.qt.io/). + +************************************************************************************ +QHotkey + +Copyright (c) 2016, Felix Barz +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of QHotkey nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ************************************************************************************ framelesshelper diff --git a/example/example.qrc b/example/example.qrc index f69b7c55..643cb5bd 100644 --- a/example/example.qrc +++ b/example/example.qrc @@ -209,6 +209,7 @@ qml/window/FluentInitializrWindow.qml qml/page/T_OpenGL.qml qml/page/T_Icons.qml + qml/window/HotkeyWindow.qml diff --git a/example/example_en_US.ts b/example/example_en_US.ts index 05473f57..8eb7b8d9 100644 --- a/example/example_en_US.ts +++ b/example/example_en_US.ts @@ -1,6 +1,54 @@ + + App + + + Quit + + + + + Test1 + + + + + Test2 + + + + + Test3 + + + + + Test4 + + + + + Test5 + + + + + Test6 + + + + + Test7 + + + + + Test8 + + + CodeExpander @@ -71,6 +119,14 @@ + + HotkeyWindow + + + Hotkey + + + HotloadWindow diff --git a/example/example_zh_CN.ts b/example/example_zh_CN.ts index a3a1fc05..bd23e4ad 100644 --- a/example/example_zh_CN.ts +++ b/example/example_zh_CN.ts @@ -1,6 +1,54 @@ + + App + + + Quit + 退出 + + + + Test1 + 测试1 + + + + Test2 + 测试2 + + + + Test3 + 测试3 + + + + Test4 + 测试4 + + + + Test5 + 测试5 + + + + Test6 + 测试6 + + + + Test7 + 测试7 + + + + Test8 + 测试8 + + CodeExpander @@ -71,6 +119,14 @@ 创建 + + HotkeyWindow + + + Hotkey + + + HotloadWindow @@ -2043,6 +2099,10 @@ Some contents... ShortcutPicker 快捷键选择器 + + Quit + 退出 + Activate the Shortcut 激活快捷键 diff --git a/example/qml/App.qml b/example/qml/App.qml index 6d33206d..2f4e8cda 100644 --- a/example/qml/App.qml +++ b/example/qml/App.qml @@ -43,7 +43,8 @@ FluLauncher { "/singleTaskWindow":"qrc:/example/qml/window/SingleTaskWindow.qml", "/standardWindow":"qrc:/example/qml/window/StandardWindow.qml", "/singleInstanceWindow":"qrc:/example/qml/window/SingleInstanceWindow.qml", - "/pageWindow":"qrc:/example/qml/window/PageWindow.qml" + "/pageWindow":"qrc:/example/qml/window/PageWindow.qml", + "/hotkey":"qrc:/example/qml/window/HotkeyWindow.qml" } var args = Qt.application.arguments if(args.length>=2 && args[1].startsWith("-crashed=")){ @@ -52,4 +53,73 @@ FluLauncher { FluRouter.navigate("/") } } + + property alias hotkeys: object_hotkey + FluObject{ + id: object_hotkey + FluHotkey{ + name: qsTr("Quit") + sequence: "Ctrl+Alt+Q" + onActivated: { + FluRouter.exit() + } + } + FluHotkey{ + name: qsTr("Test1") + sequence: "Alt+A" + onActivated: { + FluRouter.navigate("/hotkey",{sequence:sequence}) + } + } + FluHotkey{ + name: qsTr("Test2") + sequence: "Alt+B" + onActivated: { + FluRouter.navigate("/hotkey",{sequence:sequence}) + } + } + FluHotkey{ + name: qsTr("Test3") + sequence: "Alt+C" + onActivated: { + FluRouter.navigate("/hotkey",{sequence:sequence}) + } + } + FluHotkey{ + name: qsTr("Test4") + sequence: "Alt+D" + onActivated: { + FluRouter.navigate("/hotkey",{sequence:sequence}) + } + } + FluHotkey{ + name: qsTr("Test5") + sequence: "Alt+E" + onActivated: { + FluRouter.navigate("/hotkey",{sequence:sequence}) + } + } + FluHotkey{ + name: qsTr("Test6") + sequence: "Alt+F" + onActivated: { + FluRouter.navigate("/hotkey",{sequence:sequence}) + } + } + FluHotkey{ + name: qsTr("Test7") + sequence: "Alt+G" + onActivated: { + FluRouter.navigate("/hotkey",{sequence:sequence}) + } + } + FluHotkey{ + name: qsTr("Test8") + sequence: "Alt+H" + onActivated: { + FluRouter.navigate("/hotkey",{sequence:sequence}) + } + } + } + } diff --git a/example/qml/page/T_ShortcutPicker.qml b/example/qml/page/T_ShortcutPicker.qml index d82edeb3..8e5f8f0a 100644 --- a/example/qml/page/T_ShortcutPicker.qml +++ b/example/qml/page/T_ShortcutPicker.qml @@ -11,12 +11,26 @@ FluScrollablePage{ FluFrame{ Layout.fillWidth: true - Layout.preferredHeight: 100 - padding: 10 - FluShortcutPicker{ + Layout.preferredHeight: childrenRect.height + ColumnLayout{ anchors.verticalCenter: parent.verticalCenter + Item{ + Layout.preferredHeight: 15 + } + Repeater{ + model: FluApp.launcher.hotkeys.children + delegate: FluShortcutPicker{ + text: model.name + syncHotkey: FluApp.launcher.hotkeys.children[index] + Layout.leftMargin: 15 + } + } + Item{ + Layout.preferredHeight: 15 + } } } + CodeExpander{ Layout.fillWidth: true Layout.topMargin: -6 diff --git a/example/qml/window/HotkeyWindow.qml b/example/qml/window/HotkeyWindow.qml new file mode 100644 index 00000000..8c31d266 --- /dev/null +++ b/example/qml/window/HotkeyWindow.qml @@ -0,0 +1,26 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import FluentUI 1.0 +import "../component" + +FluWindow { + + id: window + property string sequence: "" + title: qsTr("Hotkey") + width: 250 + height: 250 + fixSize: true + launchMode: FluWindowType.SingleInstance + onInitArgument: + (argument)=>{ + window.sequence = argument.sequence + } + FluText{ + anchors.centerIn: parent + color: FluTheme.primaryColor + font: FluTextStyle.Title + text: window.sequence + } +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c4c40357..965227ac 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -61,9 +61,28 @@ file(COPY ${QM_FILE_PATHS} DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/Qt${QT_VERSI file(GLOB_RECURSE CPP_FILES *.cpp *.h *.cxx) foreach (filepath ${CPP_FILES}) string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath}) + message(${filename}) list(APPEND sources_files ${filename}) endforeach (filepath) +list(REMOVE_ITEM sources_files qhotkey/qhotkey_mac.cpp qhotkey/qhotkey_win.cpp qhotkey/qhotkey_x11.cpp) + +if (WIN32) + list(APPEND sources_files qhotkey/qhotkey_win.cpp) +elseif (APPLE) + list(APPEND sources_files qhotkey/qhotkey_mac.cpp) +elseif (UNIX) + list(APPEND sources_files qhotkey/qhotkey_x11.cpp) +endif() + +if (WIN32) + set(FLUENTUI_VERSION_RC_PATH ${CMAKE_CURRENT_BINARY_DIR}/version_${PROJECT_NAME}.rc) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/.cmake/version_dll.rc.in + ${FLUENTUI_VERSION_RC_PATH} + ) +endif () + if (QT_VERSION VERSION_GREATER_EQUAL "6.2") #删除fluentuiplugin.cpp与fluentuiplugin.h,这些只要Qt5使用,Qt6不需要 list(REMOVE_ITEM sources_files fluentuiplugin.h fluentuiplugin.cpp) diff --git a/src/FluApp.cpp b/src/FluApp.cpp index 82226782..a789365a 100644 --- a/src/FluApp.cpp +++ b/src/FluApp.cpp @@ -16,9 +16,10 @@ FluApp::FluApp(QObject *parent) : QObject{parent} { FluApp::~FluApp() = default; -void FluApp::init(QObject *target, QLocale locale) { +void FluApp::init(QObject *launcher, QLocale locale) { + this->launcher(launcher); _locale = std::move(locale); - _engine = qmlEngine(target); + _engine = qmlEngine(launcher); _translator = new QTranslator(this); QGuiApplication::installTranslator(_translator); const QStringList uiLanguages = _locale.uiLanguages(); diff --git a/src/FluApp.h b/src/FluApp.h index f691af61..f3d47ab7 100644 --- a/src/FluApp.h +++ b/src/FluApp.h @@ -21,6 +21,7 @@ Q_OBJECT Q_PROPERTY_AUTO(bool, useSystemAppBar) Q_PROPERTY_AUTO(QString, windowIcon) Q_PROPERTY_AUTO(QLocale, locale) +Q_PROPERTY_AUTO_P(QObject*,launcher) QML_NAMED_ELEMENT(FluApp) QML_SINGLETON @@ -34,7 +35,7 @@ SINGLETON(FluApp) static FluApp *create(QQmlEngine *, QJSEngine *) { return getInstance(); } - Q_INVOKABLE void init(QObject *target, QLocale locale = QLocale::system()); + Q_INVOKABLE void init(QObject *launcher, QLocale locale = QLocale::system()); [[maybe_unused]] Q_INVOKABLE static QJsonArray iconData(const QString &keyword = ""); diff --git a/src/FluHotkey.cpp b/src/FluHotkey.cpp new file mode 100644 index 00000000..9744b573 --- /dev/null +++ b/src/FluHotkey.cpp @@ -0,0 +1,32 @@ +#include "FluHotkey.h" + + +#include "QGuiApplication" + +FluHotkey::FluHotkey(QObject *parent) + : QObject{parent} +{ + _sequence = ""; + _isRegistered = false; + connect(this,&FluHotkey::sequenceChanged,this,[=]{ + if(_hotkey){ + delete _hotkey; + _hotkey = nullptr; + } + _hotkey = new QHotkey(QKeySequence(_sequence), true, qApp); + this->isRegistered(_hotkey->isRegistered()); + QObject::connect(_hotkey, &QHotkey::activated, qApp, [=](){ + Q_EMIT this->activated(); + }); + QObject::connect(_hotkey, &QHotkey::registeredChanged, qApp, [=](){ + this->isRegistered(_hotkey->isRegistered()); + }); + }); +} + +FluHotkey::~FluHotkey(){ + if(_hotkey){ + delete _hotkey; + _hotkey = nullptr; + } +} diff --git a/src/FluHotkey.h b/src/FluHotkey.h new file mode 100644 index 00000000..56816abf --- /dev/null +++ b/src/FluHotkey.h @@ -0,0 +1,24 @@ +#ifndef FLUHOTKEY_H +#define FLUHOTKEY_H + +#include +#include +#include "qhotkey/qhotkey.h" +#include "stdafx.h" + +class FluHotkey : public QObject +{ + Q_OBJECT + Q_PROPERTY_AUTO(QString,sequence) + Q_PROPERTY_AUTO(QString,name) + Q_PROPERTY_READONLY_AUTO(bool,isRegistered) + QML_NAMED_ELEMENT(FluHotkey) +public: + explicit FluHotkey(QObject *parent = nullptr); + ~FluHotkey(); + Q_SIGNAL void activated(); +private: + QHotkey* _hotkey = nullptr; +}; + +#endif // FLUHOTKEY_H diff --git a/src/FluentUI.cpp b/src/FluentUI.cpp index 6ea903f0..7646fbe4 100644 --- a/src/FluentUI.cpp +++ b/src/FluentUI.cpp @@ -16,6 +16,7 @@ #include "FluTableSortProxyModel.h" #include "FluFrameless.h" #include "FluTableModel.h" +#include "FluHotkey.h" void FluentUI::registerTypes(QQmlEngine *engine) { initializeEngine(engine, _uri); @@ -36,6 +37,7 @@ void FluentUI::registerTypes(const char *uri) const { qmlRegisterType(uri, major, minor, "FluTableModel"); qmlRegisterType(uri, major, minor, "FluRectangle"); qmlRegisterType(uri, major, minor, "FluFrameless"); + qmlRegisterType(uri, major, minor, "FluHotkey"); qmlRegisterType(uri, major, minor, "FluTableSortProxyModel"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluAcrylic.qml"), uri, major, minor, "FluAcrylic"); diff --git a/src/Qt5/imports/FluentUI/Controls/FluMenu.qml b/src/Qt5/imports/FluentUI/Controls/FluMenu.qml index c0c7cf1b..2e90ec89 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluMenu.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluMenu.qml @@ -39,7 +39,7 @@ T.Menu { : false clip: true currentIndex: control.currentIndex - ScrollIndicator.vertical: ScrollIndicator {} + ScrollBar.vertical: FluScrollBar{} } background: Rectangle { implicitWidth: 150 diff --git a/src/Qt5/imports/FluentUI/Controls/FluShortcutPicker.qml b/src/Qt5/imports/FluentUI/Controls/FluShortcutPicker.qml index f3d0aaa0..c7099c8a 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluShortcutPicker.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluShortcutPicker.qml @@ -10,7 +10,30 @@ FluIconButton { property string positiveText: qsTr("Save") property string neutralText: qsTr("Cancel") property string negativeText: qsTr("Reset") + property bool registered: true + property color errorColor: Qt.rgba(250/255,85/255,85/255,1) + property FluHotkey syncHotkey: undefined signal accepted() + padding: 0 + verticalPadding: 0 + horizontalPadding: 0 + onSyncHotkeyChanged: { + current = syncHotkey.sequence.split("+") + control.registered = syncHotkey.isRegistered + control.registered = Qt.binding(function(){ + return syncHotkey.isRegistered + }) + } + text: "" + color: { + if(!enabled){ + return disableColor + } + if(pressed){ + return pressedColor + } + return hovered ? hoverColor : normalColor + } QtObject{ id: d function keyToString(key_code,shift = true) @@ -112,15 +135,80 @@ FluIconButton { return ""; } } - background: Rectangle{ - border.color: FluTheme.dark ? "#505050" : "#DFDFDF" - border.width: 1 + background: Item{ implicitHeight: 42 - implicitWidth: layout_row.width+28 - radius: control.radius - color:control.color - FluFocusRectangle{ - visible: control.activeFocus + implicitWidth: 42 + } + contentItem: Item{ + implicitWidth: childrenRect.width + implicitHeight: layout_row.height + + FluText{ + id: text_title + text: control.text + visible: control.text !== "" + rightPadding: 8 + anchors{ + verticalCenter: layout_rect.verticalCenter + } + } + + Rectangle{ + id: layout_rect + border.color: FluTheme.dark ? "#505050" : "#DFDFDF" + border.width: 1 + radius: control.radius + color: control.color + height: control.height + width: layout_row.width + anchors{ + left: text_title.right + } + FluFocusRectangle{ + visible: control.activeFocus + } + Row{ + id:layout_row + spacing: 5 + anchors.centerIn: parent + Item{ + width: 8 + height: 1 + } + Repeater{ + model: control.current + delegate: Loader{ + property var keyText: modelData + sourceComponent: com_item_key + } + } + Item{ + width: 3 + height: 1 + } + FluIcon{ + iconSource: FluentIcons.EditMirrored + iconSize: 13 + anchors{ + verticalCenter: parent.verticalCenter + } + } + Item{ + width: 8 + height: 1 + } + } + } + FluText{ + id: text_error + text: qsTr("Conflict") + color: control.errorColor + visible: !control.registered + anchors{ + verticalCenter: layout_rect.verticalCenter + left: layout_rect.right + leftMargin: 4 + } } } Component{ @@ -139,29 +227,6 @@ FluIconButton { } } } - Row{ - id:layout_row - spacing: 5 - anchors.centerIn: parent - Repeater{ - model: control.current - delegate: Loader{ - property var keyText: modelData - sourceComponent: com_item_key - } - } - Item{ - width: 3 - height: 1 - } - FluIcon{ - iconSource: FluentIcons.EditMirrored - iconSize: 13 - anchors{ - verticalCenter: parent.verticalCenter - } - } - } FluContentDialog{ id:content_dialog property var keysModel: [] @@ -179,6 +244,9 @@ FluIconButton { onPositiveClicked: { control.current = content_dialog.keysModel control.accepted() + if(control.syncHotkey){ + control.syncHotkey.sequence = control.current.join("+") + } } onNegativeClickListener: function(){ content_dialog.keysModel = control.current diff --git a/src/Qt5/imports/FluentUI/plugins.qmltypes b/src/Qt5/imports/FluentUI/plugins.qmltypes index b46cb55a..66365ab0 100644 --- a/src/Qt5/imports/FluentUI/plugins.qmltypes +++ b/src/Qt5/imports/FluentUI/plugins.qmltypes @@ -31,14 +31,15 @@ Module { Property { name: "useSystemAppBar"; type: "bool" } Property { name: "windowIcon"; type: "string" } Property { name: "locale"; type: "QLocale" } + Property { name: "launcher"; type: "QObject"; isPointer: true } Method { name: "init" - Parameter { name: "target"; type: "QObject"; isPointer: true } + Parameter { name: "launcher"; type: "QObject"; isPointer: true } Parameter { name: "locale"; type: "QLocale" } } Method { name: "init" - Parameter { name: "target"; type: "QObject"; isPointer: true } + Parameter { name: "launcher"; type: "QObject"; isPointer: true } } Method { name: "iconData" @@ -159,6 +160,16 @@ Module { } Method { name: "onDestruction" } } + Component { + name: "FluHotkey" + prototype: "QObject" + exports: ["FluentUI/FluHotkey 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "sequence"; type: "string" } + Property { name: "name"; type: "string" } + Property { name: "isRegistered"; type: "bool"; isReadonly: true } + Signal { name: "activated" } + } Component { name: "FluNavigationViewType" exports: ["FluentUI/FluNavigationViewType 1.0"] @@ -273,7 +284,6 @@ Module { prototype: "QAbstractTableModel" exports: ["FluentUI/FluTableModel 1.0"] exportMetaObjectRevisions: [0] - Property { name: "dataSourceSize"; type: "int" } Property { name: "columnSource"; type: "QList" } Property { name: "rows"; type: "QList" } Property { name: "rowCount"; type: "int"; isReadonly: true } @@ -2497,37 +2507,37 @@ Module { Property { name: "darkClickListener"; type: "QVariant" } Property { name: "buttonStayTop" - type: "FluIconButton_QMLTYPE_20" + type: "FluIconButton_QMLTYPE_18" isReadonly: true isPointer: true } Property { name: "buttonMinimize" - type: "FluIconButton_QMLTYPE_20" + type: "FluIconButton_QMLTYPE_18" isReadonly: true isPointer: true } Property { name: "buttonMaximize" - type: "FluIconButton_QMLTYPE_20" + type: "FluIconButton_QMLTYPE_18" isReadonly: true isPointer: true } Property { name: "buttonClose" - type: "FluIconButton_QMLTYPE_20" + type: "FluIconButton_QMLTYPE_18" isReadonly: true isPointer: true } Property { name: "buttonDark" - type: "FluIconButton_QMLTYPE_20" + type: "FluIconButton_QMLTYPE_18" isReadonly: true isPointer: true } Property { name: "layoutMacosButtons" - type: "FluLoader_QMLTYPE_13" + type: "FluLoader_QMLTYPE_16" isReadonly: true isPointer: true } @@ -3100,6 +3110,7 @@ Module { Parameter { name: "itemcomponent"; type: "QVariant" } Parameter { name: "duration"; type: "QVariant" } } + Method { name: "clearAllInfo"; type: "QVariant" } Property { name: "children"; type: "QObject"; isList: true; isReadonly: true } } Component { @@ -3219,15 +3230,15 @@ Module { defaultProperty: "data" Property { name: "logo"; type: "QUrl" } Property { name: "title"; type: "string" } - Property { name: "items"; type: "FluObject_QMLTYPE_173"; isPointer: true } - Property { name: "footerItems"; type: "FluObject_QMLTYPE_173"; isPointer: true } + Property { name: "items"; type: "FluObject_QMLTYPE_164"; isPointer: true } + Property { name: "footerItems"; type: "FluObject_QMLTYPE_164"; 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_49"; isPointer: true } - Property { name: "navItemExpanderRightMenu"; type: "FluMenu_QMLTYPE_49"; isPointer: true } + Property { name: "navItemRightMenu"; type: "FluMenu_QMLTYPE_36"; isPointer: true } + Property { name: "navItemExpanderRightMenu"; type: "FluMenu_QMLTYPE_36"; isPointer: true } Property { name: "navCompactWidth"; type: "int" } Property { name: "navTopMargin"; type: "int" } Property { name: "cellHeight"; type: "int" } @@ -3235,13 +3246,13 @@ Module { Property { name: "hideNavAppBar"; type: "bool" } Property { name: "buttonMenu" - type: "FluIconButton_QMLTYPE_20" + type: "FluIconButton_QMLTYPE_18" isReadonly: true isPointer: true } Property { name: "buttonBack" - type: "FluIconButton_QMLTYPE_20" + type: "FluIconButton_QMLTYPE_18" isReadonly: true isPointer: true } @@ -3539,6 +3550,8 @@ Module { Property { name: "currentIndex"; type: "int" } Property { name: "spacing"; type: "int" } Property { name: "orientation"; type: "int" } + Property { name: "disabled"; type: "bool" } + Property { name: "manuallyDisabled"; type: "bool" } } Component { prototype: "QQuickRangeSlider" @@ -3693,6 +3706,9 @@ Module { Property { name: "positiveText"; type: "string" } Property { name: "neutralText"; type: "string" } Property { name: "negativeText"; type: "string" } + Property { name: "registered"; type: "bool" } + Property { name: "errorColor"; type: "QColor" } + Property { name: "syncHotkey"; type: "FluHotkey"; isPointer: true } Signal { name: "accepted" } Property { name: "iconSize"; type: "int" } Property { name: "iconSource"; type: "int" } @@ -3826,9 +3842,12 @@ Module { Property { name: "verticalHeaderVisible"; type: "bool" } Property { name: "selectedBorderColor"; type: "QColor" } Property { name: "selectedColor"; type: "QColor" } + Property { name: "columnWidthProvider"; type: "QVariant" } + Property { name: "rowHeightProvider"; type: "QVariant" } Property { name: "rows"; type: "int"; isReadonly: true } Property { name: "columns"; type: "int"; isReadonly: true } Property { name: "current"; type: "QVariant"; isReadonly: true } + Property { name: "view"; type: "QQuickTableView"; isReadonly: true; isPointer: true } Method { name: "closeEditor"; type: "QVariant" } Method { name: "resetPosition"; type: "QVariant" } Method { @@ -4081,6 +4100,7 @@ Module { Property { name: "selectedBorderColor"; type: "QColor" } Property { name: "selectedColor"; type: "QColor" } Property { name: "current"; type: "QVariant"; isReadonly: true } + Property { name: "view"; type: "QQuickTableView"; isReadonly: true; isPointer: true } Method { name: "count"; type: "QVariant" } Method { name: "visibleCount"; type: "QVariant" } Method { @@ -4172,6 +4192,7 @@ Module { Parameter { name: "duration"; type: "QVariant" } Parameter { name: "moremsg"; type: "QVariant" } } + Method { name: "clearAllInfo"; type: "QVariant" } Method { name: "moveWindowToDesktopCenter"; type: "QVariant" } Method { name: "fixWindowSize"; type: "QVariant" } Method { @@ -4274,6 +4295,7 @@ Module { Parameter { name: "duration"; type: "QVariant" } Parameter { name: "moremsg"; type: "QVariant" } } + Method { name: "clearAllInfo"; type: "QVariant" } Method { name: "moveWindowToDesktopCenter"; type: "QVariant" } Method { name: "fixWindowSize"; type: "QVariant" } Method { diff --git a/src/Qt6/imports/FluentUI/Controls/FluMenu.qml b/src/Qt6/imports/FluentUI/Controls/FluMenu.qml index 4298d725..da363c65 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluMenu.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluMenu.qml @@ -39,7 +39,7 @@ T.Menu { : false clip: true currentIndex: control.currentIndex - ScrollIndicator.vertical: ScrollIndicator {} + ScrollBar.vertical: FluScrollBar{} } background: Rectangle { implicitWidth: 150 diff --git a/src/Qt6/imports/FluentUI/Controls/FluShortcutPicker.qml b/src/Qt6/imports/FluentUI/Controls/FluShortcutPicker.qml index 2dd376f1..664bc058 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluShortcutPicker.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluShortcutPicker.qml @@ -10,7 +10,30 @@ FluIconButton { property string positiveText: qsTr("Save") property string neutralText: qsTr("Cancel") property string negativeText: qsTr("Reset") + property bool registered: true + property color errorColor: Qt.rgba(250/255,85/255,85/255,1) + property FluHotkey syncHotkey: undefined signal accepted() + padding: 0 + verticalPadding: 0 + horizontalPadding: 0 + onSyncHotkeyChanged: { + current = syncHotkey.sequence.split("+") + control.registered = syncHotkey.isRegistered + control.registered = Qt.binding(function(){ + return syncHotkey.isRegistered + }) + } + text: "" + color: { + if(!enabled){ + return disableColor + } + if(pressed){ + return pressedColor + } + return hovered ? hoverColor : normalColor + } QtObject{ id: d function keyToString(key_code,shift = true) @@ -112,15 +135,80 @@ FluIconButton { return ""; } } - background: Rectangle{ - border.color: FluTheme.dark ? "#505050" : "#DFDFDF" - border.width: 1 + background: Item{ implicitHeight: 42 - implicitWidth: layout_row.width+28 - radius: control.radius - color:control.color - FluFocusRectangle{ - visible: control.activeFocus + implicitWidth: 42 + } + contentItem: Item{ + implicitWidth: childrenRect.width + implicitHeight: layout_row.height + + FluText{ + id: text_title + text: control.text + visible: control.text !== "" + rightPadding: 8 + anchors{ + verticalCenter: layout_rect.verticalCenter + } + } + + Rectangle{ + id: layout_rect + border.color: FluTheme.dark ? "#505050" : "#DFDFDF" + border.width: 1 + radius: control.radius + color: control.color + height: control.height + width: layout_row.width + anchors{ + left: text_title.right + } + FluFocusRectangle{ + visible: control.activeFocus + } + Row{ + id:layout_row + spacing: 5 + anchors.centerIn: parent + Item{ + width: 8 + height: 1 + } + Repeater{ + model: control.current + delegate: Loader{ + property var keyText: modelData + sourceComponent: com_item_key + } + } + Item{ + width: 3 + height: 1 + } + FluIcon{ + iconSource: FluentIcons.EditMirrored + iconSize: 13 + anchors{ + verticalCenter: parent.verticalCenter + } + } + Item{ + width: 8 + height: 1 + } + } + } + FluText{ + id: text_error + text: qsTr("Conflict") + color: control.errorColor + visible: !control.registered + anchors{ + verticalCenter: layout_rect.verticalCenter + left: layout_rect.right + leftMargin: 4 + } } } Component{ @@ -139,29 +227,6 @@ FluIconButton { } } } - Row{ - id:layout_row - spacing: 5 - anchors.centerIn: parent - Repeater{ - model: control.current - delegate: Loader{ - property var keyText: modelData - sourceComponent: com_item_key - } - } - Item{ - width: 3 - height: 1 - } - FluIcon{ - iconSource: FluentIcons.EditMirrored - iconSize: 13 - anchors{ - verticalCenter: parent.verticalCenter - } - } - } FluContentDialog{ id:content_dialog property var keysModel: [] @@ -179,6 +244,9 @@ FluIconButton { onPositiveClicked: { control.current = content_dialog.keysModel control.accepted() + if(control.syncHotkey){ + control.syncHotkey.sequence = control.current.join("+") + } } onNegativeClickListener: function(){ content_dialog.keysModel = control.current diff --git a/src/fluentui_en_US.ts b/src/fluentui_en_US.ts index e4622fe4..11537df9 100644 --- a/src/fluentui_en_US.ts +++ b/src/fluentui_en_US.ts @@ -74,49 +74,49 @@ FluColorPicker - + Cancel - + OK - + Color Picker - + Edit Color - + Red - + Green - + Blue - + Opacity @@ -187,14 +187,14 @@ FluPagination - - + + <Previous - - + + Next> @@ -231,6 +231,11 @@ Reset + + + Conflict + + FluStatusLayout @@ -349,10 +354,23 @@ FluWindow - - + + Loading... + + QHotkey + + + Failed to register %1. Error: %2 + + + + + Failed to unregister %1. Error: %2 + + + diff --git a/src/fluentui_zh_CN.ts b/src/fluentui_zh_CN.ts index 4cd85268..36bc136b 100644 --- a/src/fluentui_zh_CN.ts +++ b/src/fluentui_zh_CN.ts @@ -74,49 +74,49 @@ FluColorPicker - + Cancel 取消 - + OK 确定 - + Color Picker 颜色选择器 - + Edit Color 编辑颜色 - + Red 红色 - + Green 绿色 - + Blue 蓝色 - + Opacity 透明度 @@ -187,14 +187,14 @@ FluPagination - - + + <Previous <上一页 - - + + Next> 下一页> @@ -231,6 +231,11 @@ Reset 重置 + + + Conflict + 冲突 + FluStatusLayout @@ -349,10 +354,23 @@ FluWindow - - + + Loading... 加载中... + + QHotkey + + + Failed to register %1. Error: %2 + + + + + Failed to unregister %1. Error: %2 + + + diff --git a/src/qhotkey/qhotkey.cpp b/src/qhotkey/qhotkey.cpp new file mode 100644 index 00000000..3b76d9c6 --- /dev/null +++ b/src/qhotkey/qhotkey.cpp @@ -0,0 +1,377 @@ +#include "qhotkey.h" +#include "qhotkey_p.h" +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(logQHotkey, "QHotkey") + +void QHotkey::addGlobalMapping(const QKeySequence &shortcut, QHotkey::NativeShortcut nativeShortcut) +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + const int key = shortcut[0].toCombined(); +#else + const int key = shortcut[0]; +#endif + + QMetaObject::invokeMethod(QHotkeyPrivate::instance(), "addMappingInvoked", Qt::QueuedConnection, + Q_ARG(Qt::Key, Qt::Key(key & ~Qt::KeyboardModifierMask)), + Q_ARG(Qt::KeyboardModifiers, Qt::KeyboardModifiers(key & Qt::KeyboardModifierMask)), + Q_ARG(QHotkey::NativeShortcut, nativeShortcut)); +} + +bool QHotkey::isPlatformSupported() +{ + return QHotkeyPrivate::isPlatformSupported(); +} + +QHotkey::QHotkey(QObject *parent) : + QObject(parent), + _keyCode(Qt::Key_unknown), + _modifiers(Qt::NoModifier), + _registered(false) +{} + +QHotkey::QHotkey(const QKeySequence &shortcut, bool autoRegister, QObject *parent) : + QHotkey(parent) +{ + setShortcut(shortcut, autoRegister); +} + +QHotkey::QHotkey(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, bool autoRegister, QObject *parent) : + QHotkey(parent) +{ + setShortcut(keyCode, modifiers, autoRegister); +} + +QHotkey::QHotkey(QHotkey::NativeShortcut shortcut, bool autoRegister, QObject *parent) : + QHotkey(parent) +{ + setNativeShortcut(shortcut, autoRegister); +} + +QHotkey::~QHotkey() +{ + if(_registered) + QHotkeyPrivate::instance()->removeShortcut(this); +} + +QKeySequence QHotkey::shortcut() const +{ + if(_keyCode == Qt::Key_unknown) + return QKeySequence(); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + return QKeySequence((_keyCode | _modifiers).toCombined()); +#else + return QKeySequence(static_cast(_keyCode | _modifiers)); +#endif +} + +Qt::Key QHotkey::keyCode() const +{ + return _keyCode; +} + +Qt::KeyboardModifiers QHotkey::modifiers() const +{ + return _modifiers; +} + +QHotkey::NativeShortcut QHotkey::currentNativeShortcut() const +{ + return _nativeShortcut; +} + +bool QHotkey::isRegistered() const +{ + return _registered; +} + +bool QHotkey::setShortcut(const QKeySequence &shortcut, bool autoRegister) +{ + if(shortcut.isEmpty()) + return resetShortcut(); + if(shortcut.count() > 1) { + qCWarning(logQHotkey, "Keysequences with multiple shortcuts are not allowed! " + "Only the first shortcut will be used!"); + } + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + const int key = shortcut[0].toCombined(); +#else + const int key = shortcut[0]; +#endif + + return setShortcut(Qt::Key(key & ~Qt::KeyboardModifierMask), + Qt::KeyboardModifiers(key & Qt::KeyboardModifierMask), + autoRegister); +} + +bool QHotkey::setShortcut(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, bool autoRegister) +{ + if(_registered) { + if(autoRegister) { + if(!QHotkeyPrivate::instance()->removeShortcut(this)) + return false; + } else + return false; + } + + if(keyCode == Qt::Key_unknown) { + _keyCode = Qt::Key_unknown; + _modifiers = Qt::NoModifier; + _nativeShortcut = NativeShortcut(); + return true; + } + + _keyCode = keyCode; + _modifiers = modifiers; + _nativeShortcut = QHotkeyPrivate::instance()->nativeShortcut(keyCode, modifiers); + if(_nativeShortcut.isValid()) { + if(autoRegister) + return QHotkeyPrivate::instance()->addShortcut(this); + return true; + } + + qCWarning(logQHotkey) << "Unable to map shortcut to native keys. Key:" << keyCode << "Modifiers:" << modifiers; + _keyCode = Qt::Key_unknown; + _modifiers = Qt::NoModifier; + _nativeShortcut = NativeShortcut(); + return false; +} + +bool QHotkey::resetShortcut() +{ + if(_registered && + !QHotkeyPrivate::instance()->removeShortcut(this)) { + return false; + } + + _keyCode = Qt::Key_unknown; + _modifiers = Qt::NoModifier; + _nativeShortcut = NativeShortcut(); + return true; +} + +bool QHotkey::setNativeShortcut(QHotkey::NativeShortcut nativeShortcut, bool autoRegister) +{ + if(_registered) { + if(autoRegister) { + if(!QHotkeyPrivate::instance()->removeShortcut(this)) + return false; + } else + return false; + } + + if(nativeShortcut.isValid()) { + _keyCode = Qt::Key_unknown; + _modifiers = Qt::NoModifier; + _nativeShortcut = nativeShortcut; + if(autoRegister) + return QHotkeyPrivate::instance()->addShortcut(this); + return true; + } + + _keyCode = Qt::Key_unknown; + _modifiers = Qt::NoModifier; + _nativeShortcut = NativeShortcut(); + return true; +} + +bool QHotkey::setRegistered(bool registered) +{ + if(_registered && !registered) + return QHotkeyPrivate::instance()->removeShortcut(this); + if(!_registered && registered) { + if(!_nativeShortcut.isValid()) + return false; + return QHotkeyPrivate::instance()->addShortcut(this); + } + return true; +} + + + +// ---------- QHotkeyPrivate implementation ---------- + +QHotkeyPrivate::QHotkeyPrivate() +{ + Q_ASSERT_X(qApp, Q_FUNC_INFO, "QHotkey requires QCoreApplication to be instantiated"); + qApp->eventDispatcher()->installNativeEventFilter(this); +} + +QHotkeyPrivate::~QHotkeyPrivate() +{ + if(!shortcuts.isEmpty()) + qCWarning(logQHotkey) << "QHotkeyPrivate destroyed with registered shortcuts!"; + if(qApp && qApp->eventDispatcher()) + qApp->eventDispatcher()->removeNativeEventFilter(this); +} + +QHotkey::NativeShortcut QHotkeyPrivate::nativeShortcut(Qt::Key keycode, Qt::KeyboardModifiers modifiers) +{ + Qt::ConnectionType conType = (QThread::currentThread() == thread() ? + Qt::DirectConnection : + Qt::BlockingQueuedConnection); + QHotkey::NativeShortcut res; + if(!QMetaObject::invokeMethod(this, "nativeShortcutInvoked", conType, + Q_RETURN_ARG(QHotkey::NativeShortcut, res), + Q_ARG(Qt::Key, keycode), + Q_ARG(Qt::KeyboardModifiers, modifiers))) { + return QHotkey::NativeShortcut(); + } + return res; +} + +bool QHotkeyPrivate::addShortcut(QHotkey *hotkey) +{ + if(hotkey->_registered) + return false; + + Qt::ConnectionType conType = (QThread::currentThread() == thread() ? + Qt::DirectConnection : + Qt::BlockingQueuedConnection); + bool res = false; + if(!QMetaObject::invokeMethod(this, "addShortcutInvoked", conType, + Q_RETURN_ARG(bool, res), + Q_ARG(QHotkey*, hotkey))) { + return false; + } + + if(res) + emit hotkey->registeredChanged(true); + return res; +} + +bool QHotkeyPrivate::removeShortcut(QHotkey *hotkey) +{ + if(!hotkey->_registered) + return false; + + Qt::ConnectionType conType = (QThread::currentThread() == thread() ? + Qt::DirectConnection : + Qt::BlockingQueuedConnection); + bool res = false; + if(!QMetaObject::invokeMethod(this, "removeShortcutInvoked", conType, + Q_RETURN_ARG(bool, res), + Q_ARG(QHotkey*, hotkey))) { + return false; + } + + if(res) + emit hotkey->registeredChanged(false); + return res; +} + +void QHotkeyPrivate::activateShortcut(QHotkey::NativeShortcut shortcut) +{ + QMetaMethod signal = QMetaMethod::fromSignal(&QHotkey::activated); + for(QHotkey *hkey : shortcuts.values(shortcut)) + signal.invoke(hkey, Qt::QueuedConnection); +} + +void QHotkeyPrivate::releaseShortcut(QHotkey::NativeShortcut shortcut) +{ + QMetaMethod signal = QMetaMethod::fromSignal(&QHotkey::released); + for(QHotkey *hkey : shortcuts.values(shortcut)) + signal.invoke(hkey, Qt::QueuedConnection); +} + +void QHotkeyPrivate::addMappingInvoked(Qt::Key keycode, Qt::KeyboardModifiers modifiers, QHotkey::NativeShortcut nativeShortcut) +{ + mapping.insert({keycode, modifiers}, nativeShortcut); +} + +bool QHotkeyPrivate::addShortcutInvoked(QHotkey *hotkey) +{ + QHotkey::NativeShortcut shortcut = hotkey->_nativeShortcut; + + if(!shortcuts.contains(shortcut)) { + if(!registerShortcut(shortcut)) { + qCWarning(logQHotkey) << QHotkey::tr("Failed to register %1. Error: %2").arg(hotkey->shortcut().toString(), error); + return false; + } + } + + shortcuts.insert(shortcut, hotkey); + hotkey->_registered = true; + return true; +} + +bool QHotkeyPrivate::removeShortcutInvoked(QHotkey *hotkey) +{ + QHotkey::NativeShortcut shortcut = hotkey->_nativeShortcut; + + if(shortcuts.remove(shortcut, hotkey) == 0) + return false; + hotkey->_registered = false; + emit hotkey->registeredChanged(true); + if(shortcuts.count(shortcut) == 0) { + if (!unregisterShortcut(shortcut)) { + qCWarning(logQHotkey) << QHotkey::tr("Failed to unregister %1. Error: %2").arg(hotkey->shortcut().toString(), error); + return false; + } + return true; + } + return true; +} + +QHotkey::NativeShortcut QHotkeyPrivate::nativeShortcutInvoked(Qt::Key keycode, Qt::KeyboardModifiers modifiers) +{ + if(mapping.contains({keycode, modifiers})) + return mapping.value({keycode, modifiers}); + + bool ok1 = false; + auto k = nativeKeycode(keycode, ok1); + bool ok2 = false; + auto m = nativeModifiers(modifiers, ok2); + if(ok1 && ok2) + return {k, m}; + return {}; +} + + + +QHotkey::NativeShortcut::NativeShortcut() : + key(), + modifier(), + valid(false) +{} + +QHotkey::NativeShortcut::NativeShortcut(quint32 key, quint32 modifier) : + key(key), + modifier(modifier), + valid(true) +{} + +bool QHotkey::NativeShortcut::isValid() const +{ + return valid; +} + +bool QHotkey::NativeShortcut::operator ==(QHotkey::NativeShortcut other) const +{ + return (key == other.key) && + (modifier == other.modifier) && + valid == other.valid; +} + +bool QHotkey::NativeShortcut::operator !=(QHotkey::NativeShortcut other) const +{ + return (key != other.key) || + (modifier != other.modifier) || + valid != other.valid; +} + +QHOTKEY_HASH_SEED qHash(QHotkey::NativeShortcut key) +{ + return qHash(key.key) ^ qHash(key.modifier); +} + +QHOTKEY_HASH_SEED qHash(QHotkey::NativeShortcut key, QHOTKEY_HASH_SEED seed) +{ + return qHash(key.key, seed) ^ qHash(key.modifier, seed); +} diff --git a/src/qhotkey/qhotkey.h b/src/qhotkey/qhotkey.h new file mode 100644 index 00000000..3697c8e3 --- /dev/null +++ b/src/qhotkey/qhotkey.h @@ -0,0 +1,130 @@ +#ifndef QHOTKEY_H +#define QHOTKEY_H + +#include +#include +#include +#include + +#ifdef QHOTKEY_SHARED +# ifdef QHOTKEY_LIBRARY +# define QHOTKEY_EXPORT Q_DECL_EXPORT +# else +# define QHOTKEY_EXPORT Q_DECL_IMPORT +# endif +#else +# define QHOTKEY_EXPORT +#endif + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + #define QHOTKEY_HASH_SEED size_t +#else + #define QHOTKEY_HASH_SEED uint +#endif + +//! A class to define global, systemwide Hotkeys +class QHOTKEY_EXPORT QHotkey : public QObject +{ + Q_OBJECT + //! @private + friend class QHotkeyPrivate; + + //! Specifies whether this hotkey is currently registered or not + Q_PROPERTY(bool registered READ isRegistered WRITE setRegistered NOTIFY registeredChanged) + //! Holds the shortcut this hotkey will be triggered on + Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut RESET resetShortcut) + +public: + //! Defines shortcut with native keycodes + class QHOTKEY_EXPORT NativeShortcut { + public: + //! The native keycode + quint32 key; + //! The native modifiers + quint32 modifier; + + //! Creates an invalid native shortcut + NativeShortcut(); + //! Creates a valid native shortcut, with the given key and modifiers + NativeShortcut(quint32 key, quint32 modifier = 0); + + //! Checks, whether this shortcut is valid or not + bool isValid() const; + + //! Equality operator + bool operator ==(NativeShortcut other) const; + //! Inequality operator + bool operator !=(NativeShortcut other) const; + + private: + bool valid; + }; + + //! Adds a global mapping of a key sequence to a replacement native shortcut + static void addGlobalMapping(const QKeySequence &shortcut, NativeShortcut nativeShortcut); + + //! Checks if global shortcuts are supported by the current platform + static bool isPlatformSupported(); + + //! Default Constructor + explicit QHotkey(QObject *parent = nullptr); + //! Constructs a hotkey with a shortcut and optionally registers it + explicit QHotkey(const QKeySequence &shortcut, bool autoRegister = false, QObject *parent = nullptr); + //! Constructs a hotkey with a key and modifiers and optionally registers it + explicit QHotkey(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, bool autoRegister = false, QObject *parent = nullptr); + //! Constructs a hotkey from a native shortcut and optionally registers it + explicit QHotkey(NativeShortcut shortcut, bool autoRegister = false, QObject *parent = nullptr); + ~QHotkey() override; + + //! @readAcFn{QHotkey::registered} + bool isRegistered() const; + //! @readAcFn{QHotkey::shortcut} + QKeySequence shortcut() const; + //! @readAcFn{QHotkey::shortcut} - the key only + Qt::Key keyCode() const; + //! @readAcFn{QHotkey::shortcut} - the modifiers only + Qt::KeyboardModifiers modifiers() const; + + //! Get the current native shortcut + NativeShortcut currentNativeShortcut() const; + +public slots: + //! @writeAcFn{QHotkey::registered} + bool setRegistered(bool registered); + + //! @writeAcFn{QHotkey::shortcut} + bool setShortcut(const QKeySequence &shortcut, bool autoRegister = false); + //! @writeAcFn{QHotkey::shortcut} + bool setShortcut(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, bool autoRegister = false); + //! @resetAcFn{QHotkey::shortcut} + bool resetShortcut(); + + //! Set this hotkey to a native shortcut + bool setNativeShortcut(QHotkey::NativeShortcut nativeShortcut, bool autoRegister = false); + +signals: + //! Will be emitted if the shortcut is pressed + void activated(QPrivateSignal); + + //! Will be emitted if the shortcut press is released + void released(QPrivateSignal); + + //! @notifyAcFn{QHotkey::registered} + void registeredChanged(bool registered); + +private: + Qt::Key _keyCode; + Qt::KeyboardModifiers _modifiers; + + NativeShortcut _nativeShortcut; + bool _registered; +}; + +QHOTKEY_HASH_SEED QHOTKEY_EXPORT qHash(QHotkey::NativeShortcut key); +QHOTKEY_HASH_SEED QHOTKEY_EXPORT qHash(QHotkey::NativeShortcut key, QHOTKEY_HASH_SEED seed); + +QHOTKEY_EXPORT Q_DECLARE_LOGGING_CATEGORY(logQHotkey) + +Q_DECLARE_METATYPE(QHotkey::NativeShortcut) + +#endif // QHOTKEY_H diff --git a/src/qhotkey/qhotkey_mac.cpp b/src/qhotkey/qhotkey_mac.cpp new file mode 100644 index 00000000..799f5153 --- /dev/null +++ b/src/qhotkey/qhotkey_mac.cpp @@ -0,0 +1,291 @@ +#include "qhotkey.h" +#include "qhotkey_p.h" +#include +#include + +class QHotkeyPrivateMac : public QHotkeyPrivate +{ +public: + // QAbstractNativeEventFilter interface + bool nativeEventFilter(const QByteArray &eventType, void *message, _NATIVE_EVENT_RESULT *result) override; + + static OSStatus hotkeyPressEventHandler(EventHandlerCallRef nextHandler, EventRef event, void* data); + static OSStatus hotkeyReleaseEventHandler(EventHandlerCallRef nextHandler, EventRef event, void* data); + +protected: + // QHotkeyPrivate interface + quint32 nativeKeycode(Qt::Key keycode, bool &ok) Q_DECL_OVERRIDE; + quint32 nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok) Q_DECL_OVERRIDE; + bool registerShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE; + bool unregisterShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE; + +private: + static bool isHotkeyHandlerRegistered; + static QHash hotkeyRefs; +}; +NATIVE_INSTANCE(QHotkeyPrivateMac) + +bool QHotkeyPrivate::isPlatformSupported() +{ + return true; +} + +bool QHotkeyPrivateMac::isHotkeyHandlerRegistered = false; +QHash QHotkeyPrivateMac::hotkeyRefs; + +bool QHotkeyPrivateMac::nativeEventFilter(const QByteArray &eventType, void *message, _NATIVE_EVENT_RESULT *result) +{ + Q_UNUSED(eventType) + Q_UNUSED(message) + Q_UNUSED(result) + return false; +} + +quint32 QHotkeyPrivateMac::nativeKeycode(Qt::Key keycode, bool &ok) +{ + // Constants found in NSEvent.h from AppKit.framework + ok = true; + switch (keycode) { + case Qt::Key_Return: + return kVK_Return; + case Qt::Key_Enter: + return kVK_ANSI_KeypadEnter; + case Qt::Key_Tab: + return kVK_Tab; + case Qt::Key_Space: + return kVK_Space; + case Qt::Key_Backspace: + return kVK_Delete; + case Qt::Key_Escape: + return kVK_Escape; + case Qt::Key_CapsLock: + return kVK_CapsLock; + case Qt::Key_Option: + return kVK_Option; + case Qt::Key_F17: + return kVK_F17; + case Qt::Key_VolumeUp: + return kVK_VolumeUp; + case Qt::Key_VolumeDown: + return kVK_VolumeDown; + case Qt::Key_F18: + return kVK_F18; + case Qt::Key_F19: + return kVK_F19; + case Qt::Key_F20: + return kVK_F20; + case Qt::Key_F5: + return kVK_F5; + case Qt::Key_F6: + return kVK_F6; + case Qt::Key_F7: + return kVK_F7; + case Qt::Key_F3: + return kVK_F3; + case Qt::Key_F8: + return kVK_F8; + case Qt::Key_F9: + return kVK_F9; + case Qt::Key_F11: + return kVK_F11; + case Qt::Key_F13: + return kVK_F13; + case Qt::Key_F16: + return kVK_F16; + case Qt::Key_F14: + return kVK_F14; + case Qt::Key_F10: + return kVK_F10; + case Qt::Key_F12: + return kVK_F12; + case Qt::Key_F15: + return kVK_F15; + case Qt::Key_Help: + return kVK_Help; + case Qt::Key_Home: + return kVK_Home; + case Qt::Key_PageUp: + return kVK_PageUp; + case Qt::Key_Delete: + return kVK_ForwardDelete; + case Qt::Key_F4: + return kVK_F4; + case Qt::Key_End: + return kVK_End; + case Qt::Key_F2: + return kVK_F2; + case Qt::Key_PageDown: + return kVK_PageDown; + case Qt::Key_F1: + return kVK_F1; + case Qt::Key_Left: + return kVK_LeftArrow; + case Qt::Key_Right: + return kVK_RightArrow; + case Qt::Key_Down: + return kVK_DownArrow; + case Qt::Key_Up: + return kVK_UpArrow; + default: + ok = false; + break; + } + + UTF16Char ch = keycode; + + CFDataRef currentLayoutData; + TISInputSourceRef currentKeyboard = TISCopyCurrentASCIICapableKeyboardLayoutInputSource(); + + if (currentKeyboard == NULL) + return 0; + + currentLayoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData); + CFRelease(currentKeyboard); + if (currentLayoutData == NULL) + return 0; + + UCKeyboardLayout* header = (UCKeyboardLayout*)CFDataGetBytePtr(currentLayoutData); + UCKeyboardTypeHeader* table = header->keyboardTypeList; + + uint8_t *data = (uint8_t*)header; + for (quint32 i=0; i < header->keyboardTypeCount; i++) { + UCKeyStateRecordsIndex* stateRec = 0; + if (table[i].keyStateRecordsIndexOffset != 0) { + stateRec = reinterpret_cast(data + table[i].keyStateRecordsIndexOffset); + if (stateRec->keyStateRecordsIndexFormat != kUCKeyStateRecordsIndexFormat) stateRec = 0; + } + + UCKeyToCharTableIndex* charTable = reinterpret_cast(data + table[i].keyToCharTableIndexOffset); + if (charTable->keyToCharTableIndexFormat != kUCKeyToCharTableIndexFormat) continue; + + for (quint32 j=0; j < charTable->keyToCharTableCount; j++) { + UCKeyOutput* keyToChar = reinterpret_cast(data + charTable->keyToCharTableOffsets[j]); + for (quint32 k=0; k < charTable->keyToCharTableSize; k++) { + if (keyToChar[k] & kUCKeyOutputTestForIndexMask) { + long idx = keyToChar[k] & kUCKeyOutputGetIndexMask; + if (stateRec && idx < stateRec->keyStateRecordCount) { + UCKeyStateRecord* rec = reinterpret_cast(data + stateRec->keyStateRecordOffsets[idx]); + if (rec->stateZeroCharData == ch) { + ok = true; + return k; + } + } + } + else if (!(keyToChar[k] & kUCKeyOutputSequenceIndexMask) && keyToChar[k] < 0xFFFE) { + if (keyToChar[k] == ch) { + ok = true; + return k; + } + } + } + } + } + return 0; +} + +quint32 QHotkeyPrivateMac::nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok) +{ + quint32 nMods = 0; + if (modifiers & Qt::ShiftModifier) + nMods |= shiftKey; + if (modifiers & Qt::ControlModifier) + nMods |= cmdKey; + if (modifiers & Qt::AltModifier) + nMods |= optionKey; + if (modifiers & Qt::MetaModifier) + nMods |= controlKey; + if (modifiers & Qt::KeypadModifier) + nMods |= kEventKeyModifierNumLockMask; + ok = true; + return nMods; +} + +bool QHotkeyPrivateMac::registerShortcut(QHotkey::NativeShortcut shortcut) +{ + if (!this->isHotkeyHandlerRegistered) + { + EventTypeSpec pressEventSpec; + pressEventSpec.eventClass = kEventClassKeyboard; + pressEventSpec.eventKind = kEventHotKeyPressed; + InstallApplicationEventHandler(&QHotkeyPrivateMac::hotkeyPressEventHandler, 1, &pressEventSpec, NULL, NULL); + + EventTypeSpec releaseEventSpec; + releaseEventSpec.eventClass = kEventClassKeyboard; + releaseEventSpec.eventKind = kEventHotKeyReleased; + InstallApplicationEventHandler(&QHotkeyPrivateMac::hotkeyReleaseEventHandler, 1, &releaseEventSpec, NULL, NULL); + } + + EventHotKeyID hkeyID; + hkeyID.signature = shortcut.key; + hkeyID.id = shortcut.modifier; + + EventHotKeyRef eventRef = 0; + OSStatus status = RegisterEventHotKey(shortcut.key, + shortcut.modifier, + hkeyID, + GetApplicationEventTarget(), + 0, + &eventRef); + if (status != noErr) { + error = QString::number(status); + return false; + } else { + this->hotkeyRefs.insert(shortcut, eventRef); + return true; + } +} + +bool QHotkeyPrivateMac::unregisterShortcut(QHotkey::NativeShortcut shortcut) +{ + EventHotKeyRef eventRef = QHotkeyPrivateMac::hotkeyRefs.value(shortcut); + OSStatus status = UnregisterEventHotKey(eventRef); + if (status != noErr) { + error = QString::number(status); + return false; + } else { + this->hotkeyRefs.remove(shortcut); + return true; + } +} + +OSStatus QHotkeyPrivateMac::hotkeyPressEventHandler(EventHandlerCallRef nextHandler, EventRef event, void* data) +{ + Q_UNUSED(nextHandler); + Q_UNUSED(data); + + if (GetEventClass(event) == kEventClassKeyboard && + GetEventKind(event) == kEventHotKeyPressed) { + EventHotKeyID hkeyID; + GetEventParameter(event, + kEventParamDirectObject, + typeEventHotKeyID, + NULL, + sizeof(EventHotKeyID), + NULL, + &hkeyID); + hotkeyPrivate->activateShortcut({hkeyID.signature, hkeyID.id}); + } + + return noErr; +} + +OSStatus QHotkeyPrivateMac::hotkeyReleaseEventHandler(EventHandlerCallRef nextHandler, EventRef event, void* data) +{ + Q_UNUSED(nextHandler); + Q_UNUSED(data); + + if (GetEventClass(event) == kEventClassKeyboard && + GetEventKind(event) == kEventHotKeyReleased) { + EventHotKeyID hkeyID; + GetEventParameter(event, + kEventParamDirectObject, + typeEventHotKeyID, + NULL, + sizeof(EventHotKeyID), + NULL, + &hkeyID); + hotkeyPrivate->releaseShortcut({hkeyID.signature, hkeyID.id}); + } + + return noErr; +} diff --git a/src/qhotkey/qhotkey_p.h b/src/qhotkey/qhotkey_p.h new file mode 100644 index 00000000..8bc5ab64 --- /dev/null +++ b/src/qhotkey/qhotkey_p.h @@ -0,0 +1,62 @@ +#ifndef QHOTKEY_P_H +#define QHOTKEY_P_H + +#include "qhotkey.h" +#include +#include +#include +#include + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + #define _NATIVE_EVENT_RESULT qintptr +#else + #define _NATIVE_EVENT_RESULT long +#endif + +class QHOTKEY_EXPORT QHotkeyPrivate : public QObject, public QAbstractNativeEventFilter +{ + Q_OBJECT + +public: + QHotkeyPrivate();//singleton!!! + ~QHotkeyPrivate(); + + static QHotkeyPrivate *instance(); + static bool isPlatformSupported(); + + QHotkey::NativeShortcut nativeShortcut(Qt::Key keycode, Qt::KeyboardModifiers modifiers); + + bool addShortcut(QHotkey *hotkey); + bool removeShortcut(QHotkey *hotkey); + +protected: + void activateShortcut(QHotkey::NativeShortcut shortcut); + void releaseShortcut(QHotkey::NativeShortcut shortcut); + + virtual quint32 nativeKeycode(Qt::Key keycode, bool &ok) = 0;//platform implement + virtual quint32 nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok) = 0;//platform implement + + virtual bool registerShortcut(QHotkey::NativeShortcut shortcut) = 0;//platform implement + virtual bool unregisterShortcut(QHotkey::NativeShortcut shortcut) = 0;//platform implement + + QString error; + +private: + QHash, QHotkey::NativeShortcut> mapping; + QMultiHash shortcuts; + + Q_INVOKABLE void addMappingInvoked(Qt::Key keycode, Qt::KeyboardModifiers modifiers, QHotkey::NativeShortcut nativeShortcut); + Q_INVOKABLE bool addShortcutInvoked(QHotkey *hotkey); + Q_INVOKABLE bool removeShortcutInvoked(QHotkey *hotkey); + Q_INVOKABLE QHotkey::NativeShortcut nativeShortcutInvoked(Qt::Key keycode, Qt::KeyboardModifiers modifiers); +}; + +#define NATIVE_INSTANCE(ClassName) \ + Q_GLOBAL_STATIC(ClassName, hotkeyPrivate) \ + \ + QHotkeyPrivate *QHotkeyPrivate::instance()\ + {\ + return hotkeyPrivate;\ + } + +#endif // QHOTKEY_P_H diff --git a/src/qhotkey/qhotkey_win.cpp b/src/qhotkey/qhotkey_win.cpp new file mode 100644 index 00000000..949d87a6 --- /dev/null +++ b/src/qhotkey/qhotkey_win.cpp @@ -0,0 +1,309 @@ +#include "qhotkey.h" +#include "qhotkey_p.h" +#include +#include +#include +#include +#include + +#define HKEY_ID(nativeShortcut) (((nativeShortcut.key ^ (nativeShortcut.modifier << 8)) & 0x0FFF) | 0x7000) + +#if !defined(MOD_NOREPEAT) +#define MOD_NOREPEAT 0x4000 +#endif + +class QHotkeyPrivateWin : public QHotkeyPrivate +{ +public: + QHotkeyPrivateWin(); + // QAbstractNativeEventFilter interface + bool nativeEventFilter(const QByteArray &eventType, void *message, _NATIVE_EVENT_RESULT *result) override; + +protected: + void pollForHotkeyRelease(); + // QHotkeyPrivate interface + quint32 nativeKeycode(Qt::Key keycode, bool &ok) Q_DECL_OVERRIDE; + quint32 nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok) Q_DECL_OVERRIDE; + bool registerShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE; + bool unregisterShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE; + +private: + static QString formatWinError(DWORD winError); + QTimer pollTimer; + QList polledShortcuts; +}; +NATIVE_INSTANCE(QHotkeyPrivateWin) + +QHotkeyPrivateWin::QHotkeyPrivateWin(){ + pollTimer.setInterval(50); + connect(&pollTimer, &QTimer::timeout, this, &QHotkeyPrivateWin::pollForHotkeyRelease); +} + +bool QHotkeyPrivate::isPlatformSupported() +{ + return true; +} + +bool QHotkeyPrivateWin::nativeEventFilter(const QByteArray &eventType, void *message, _NATIVE_EVENT_RESULT *result) +{ + Q_UNUSED(eventType) + Q_UNUSED(result) + + MSG* msg = static_cast(message); + if(msg->message == WM_HOTKEY) { + QHotkey::NativeShortcut shortcut = {HIWORD(msg->lParam), LOWORD(msg->lParam)}; + this->activateShortcut(shortcut); + if (this->polledShortcuts.empty()) + this->pollTimer.start(); + this->polledShortcuts.append(shortcut); + } + + return false; +} + +void QHotkeyPrivateWin::pollForHotkeyRelease() +{ + auto it = std::remove_if(this->polledShortcuts.begin(), this->polledShortcuts.end(), [this](const QHotkey::NativeShortcut &shortcut) { + bool pressed = (GetAsyncKeyState(shortcut.key) & (1 << 15)) != 0; + if (!pressed) + this->releaseShortcut(shortcut); + return !pressed; + }); + this->polledShortcuts.erase(it, this->polledShortcuts.end()); + if (this->polledShortcuts.empty()) + this->pollTimer.stop(); +} + +quint32 QHotkeyPrivateWin::nativeKeycode(Qt::Key keycode, bool &ok) +{ + ok = true; + if(keycode <= 0xFFFF) {//Try to obtain the key from it's "character" + const SHORT vKey = VkKeyScanW(static_cast(keycode)); + if(vKey > -1) + return LOBYTE(vKey); + } + + //find key from switch/case --> Only finds a very small subset of keys + switch (keycode) + { + case Qt::Key_Escape: + return VK_ESCAPE; + case Qt::Key_Tab: + case Qt::Key_Backtab: + return VK_TAB; + case Qt::Key_Backspace: + return VK_BACK; + case Qt::Key_Return: + case Qt::Key_Enter: + return VK_RETURN; + case Qt::Key_Insert: + return VK_INSERT; + case Qt::Key_Delete: + return VK_DELETE; + case Qt::Key_Pause: + return VK_PAUSE; + case Qt::Key_Print: + return VK_PRINT; + case Qt::Key_Clear: + return VK_CLEAR; + case Qt::Key_Home: + return VK_HOME; + case Qt::Key_End: + return VK_END; + case Qt::Key_Left: + return VK_LEFT; + case Qt::Key_Up: + return VK_UP; + case Qt::Key_Right: + return VK_RIGHT; + case Qt::Key_Down: + return VK_DOWN; + case Qt::Key_PageUp: + return VK_PRIOR; + case Qt::Key_PageDown: + return VK_NEXT; + case Qt::Key_CapsLock: + return VK_CAPITAL; + case Qt::Key_NumLock: + return VK_NUMLOCK; + case Qt::Key_ScrollLock: + return VK_SCROLL; + + case Qt::Key_F1: + return VK_F1; + case Qt::Key_F2: + return VK_F2; + case Qt::Key_F3: + return VK_F3; + case Qt::Key_F4: + return VK_F4; + case Qt::Key_F5: + return VK_F5; + case Qt::Key_F6: + return VK_F6; + case Qt::Key_F7: + return VK_F7; + case Qt::Key_F8: + return VK_F8; + case Qt::Key_F9: + return VK_F9; + case Qt::Key_F10: + return VK_F10; + case Qt::Key_F11: + return VK_F11; + case Qt::Key_F12: + return VK_F12; + case Qt::Key_F13: + return VK_F13; + case Qt::Key_F14: + return VK_F14; + case Qt::Key_F15: + return VK_F15; + case Qt::Key_F16: + return VK_F16; + case Qt::Key_F17: + return VK_F17; + case Qt::Key_F18: + return VK_F18; + case Qt::Key_F19: + return VK_F19; + case Qt::Key_F20: + return VK_F20; + case Qt::Key_F21: + return VK_F21; + case Qt::Key_F22: + return VK_F22; + case Qt::Key_F23: + return VK_F23; + case Qt::Key_F24: + return VK_F24; + + case Qt::Key_Menu: + return VK_APPS; + case Qt::Key_Help: + return VK_HELP; + case Qt::Key_MediaNext: + return VK_MEDIA_NEXT_TRACK; + case Qt::Key_MediaPrevious: + return VK_MEDIA_PREV_TRACK; + case Qt::Key_MediaPlay: + return VK_MEDIA_PLAY_PAUSE; + case Qt::Key_MediaStop: + return VK_MEDIA_STOP; + case Qt::Key_VolumeDown: + return VK_VOLUME_DOWN; + case Qt::Key_VolumeUp: + return VK_VOLUME_UP; + case Qt::Key_VolumeMute: + return VK_VOLUME_MUTE; + case Qt::Key_Mode_switch: + return VK_MODECHANGE; + case Qt::Key_Select: + return VK_SELECT; + case Qt::Key_Printer: + return VK_PRINT; + case Qt::Key_Execute: + return VK_EXECUTE; + case Qt::Key_Sleep: + return VK_SLEEP; + case Qt::Key_Period: + return VK_DECIMAL; + case Qt::Key_Play: + return VK_PLAY; + case Qt::Key_Cancel: + return VK_CANCEL; + + case Qt::Key_Forward: + return VK_BROWSER_FORWARD; + case Qt::Key_Refresh: + return VK_BROWSER_REFRESH; + case Qt::Key_Stop: + return VK_BROWSER_STOP; + case Qt::Key_Search: + return VK_BROWSER_SEARCH; + case Qt::Key_Favorites: + return VK_BROWSER_FAVORITES; + case Qt::Key_HomePage: + return VK_BROWSER_HOME; + + case Qt::Key_LaunchMail: + return VK_LAUNCH_MAIL; + case Qt::Key_LaunchMedia: + return VK_LAUNCH_MEDIA_SELECT; + case Qt::Key_Launch0: + return VK_LAUNCH_APP1; + case Qt::Key_Launch1: + return VK_LAUNCH_APP2; + + case Qt::Key_Massyo: + return VK_OEM_FJ_MASSHOU; + case Qt::Key_Touroku: + return VK_OEM_FJ_TOUROKU; + + default: + if(keycode <= 0xFFFF) + return static_cast(keycode); + else { + ok = false; + return 0; + } + } +} + +quint32 QHotkeyPrivateWin::nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok) +{ + quint32 nMods = 0; + if (modifiers & Qt::ShiftModifier) + nMods |= MOD_SHIFT; + if (modifiers & Qt::ControlModifier) + nMods |= MOD_CONTROL; + if (modifiers & Qt::AltModifier) + nMods |= MOD_ALT; + if (modifiers & Qt::MetaModifier) + nMods |= MOD_WIN; + ok = true; + return nMods; +} + +bool QHotkeyPrivateWin::registerShortcut(QHotkey::NativeShortcut shortcut) +{ + BOOL ok = RegisterHotKey(NULL, + HKEY_ID(shortcut), + shortcut.modifier + MOD_NOREPEAT, + shortcut.key); + if(ok) + return true; + else { + error = QHotkeyPrivateWin::formatWinError(::GetLastError()); + return false; + } +} + +bool QHotkeyPrivateWin::unregisterShortcut(QHotkey::NativeShortcut shortcut) +{ + BOOL ok = UnregisterHotKey(NULL, HKEY_ID(shortcut)); + if(ok) + return true; + else { + error = QHotkeyPrivateWin::formatWinError(::GetLastError()); + return false; + } +} + +QString QHotkeyPrivateWin::formatWinError(DWORD winError) +{ + wchar_t *buffer = NULL; + DWORD num = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + winError, + 0, + (LPWSTR)&buffer, + 0, + NULL); + if(buffer) { + QString res = QString::fromWCharArray(buffer, num); + LocalFree(buffer); + return res; + } else + return QString(); +} diff --git a/src/qhotkey/qhotkey_x11.cpp b/src/qhotkey/qhotkey_x11.cpp new file mode 100644 index 00000000..d3ac1d15 --- /dev/null +++ b/src/qhotkey/qhotkey_x11.cpp @@ -0,0 +1,268 @@ +#include "qhotkey.h" +#include "qhotkey_p.h" + +#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) + #include +#else + #include + #include +#endif + +#include +#include +#include +#include + +//compatibility to pre Qt 5.8 +#ifndef Q_FALLTHROUGH +#define Q_FALLTHROUGH() (void)0 +#endif + +class QHotkeyPrivateX11 : public QHotkeyPrivate +{ +public: + // QAbstractNativeEventFilter interface + bool nativeEventFilter(const QByteArray &eventType, void *message, _NATIVE_EVENT_RESULT *result) override; + +protected: + // QHotkeyPrivate interface + quint32 nativeKeycode(Qt::Key keycode, bool &ok) Q_DECL_OVERRIDE; + quint32 nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok) Q_DECL_OVERRIDE; + static QString getX11String(Qt::Key keycode); + bool registerShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE; + bool unregisterShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE; + +private: + static const QVector specialModifiers; + static const quint32 validModsMask; + xcb_key_press_event_t prevHandledEvent; + xcb_key_press_event_t prevEvent; + + static QString formatX11Error(Display *display, int errorCode); + + class HotkeyErrorHandler { + public: + HotkeyErrorHandler(); + ~HotkeyErrorHandler(); + + static bool hasError; + static QString errorString; + + private: + XErrorHandler prevHandler; + + static int handleError(Display *display, XErrorEvent *error); + }; +}; +NATIVE_INSTANCE(QHotkeyPrivateX11) + +bool QHotkeyPrivate::isPlatformSupported() +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) + return qGuiApp->nativeInterface(); +#else + return QX11Info::isPlatformX11(); +#endif +} + +const QVector QHotkeyPrivateX11::specialModifiers = {0, Mod2Mask, LockMask, (Mod2Mask | LockMask)}; +const quint32 QHotkeyPrivateX11::validModsMask = ShiftMask | ControlMask | Mod1Mask | Mod4Mask; + +bool QHotkeyPrivateX11::nativeEventFilter(const QByteArray &eventType, void *message, _NATIVE_EVENT_RESULT *result) +{ + Q_UNUSED(eventType) + Q_UNUSED(result) + + auto *genericEvent = static_cast(message); + if (genericEvent->response_type == XCB_KEY_PRESS) { + xcb_key_press_event_t keyEvent = *static_cast(message); + this->prevEvent = keyEvent; + if (this->prevHandledEvent.response_type == XCB_KEY_RELEASE) { + if(this->prevHandledEvent.time == keyEvent.time) return false; + } + this->activateShortcut({keyEvent.detail, keyEvent.state & QHotkeyPrivateX11::validModsMask}); + } else if (genericEvent->response_type == XCB_KEY_RELEASE) { + xcb_key_release_event_t keyEvent = *static_cast(message); + this->prevEvent = keyEvent; + QTimer::singleShot(50, [this, keyEvent] { + if(this->prevEvent.time == keyEvent.time && this->prevEvent.response_type == keyEvent.response_type && this->prevEvent.detail == keyEvent.detail){ + this->releaseShortcut({keyEvent.detail, keyEvent.state & QHotkeyPrivateX11::validModsMask}); + } + }); + this->prevHandledEvent = keyEvent; + } + + return false; +} + +QString QHotkeyPrivateX11::getX11String(Qt::Key keycode) +{ + switch(keycode){ + + case Qt::Key_MediaLast : + case Qt::Key_MediaPrevious : + return QStringLiteral("XF86AudioPrev"); + case Qt::Key_MediaNext : + return QStringLiteral("XF86AudioNext"); + case Qt::Key_MediaPause : + case Qt::Key_MediaPlay : + case Qt::Key_MediaTogglePlayPause : + return QStringLiteral("XF86AudioPlay"); + case Qt::Key_MediaRecord : + return QStringLiteral("XF86AudioRecord"); + case Qt::Key_MediaStop : + return QStringLiteral("XF86AudioStop"); + default : + return QKeySequence(keycode).toString(QKeySequence::NativeText); + } +} + +quint32 QHotkeyPrivateX11::nativeKeycode(Qt::Key keycode, bool &ok) +{ + QString keyString = getX11String(keycode); + + KeySym keysym = XStringToKeysym(keyString.toLatin1().constData()); + if (keysym == NoSymbol) { + //not found -> just use the key + if(keycode <= 0xFFFF) + keysym = keycode; + else + return 0; + } + +#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) + const QNativeInterface::QX11Application *x11Interface = qGuiApp->nativeInterface(); + Display *display = x11Interface->display(); +#else + const bool x11Interface = QX11Info::isPlatformX11(); + Display *display = QX11Info::display(); +#endif + + if(x11Interface) { + auto res = XKeysymToKeycode(display, keysym); + if(res != 0) + ok = true; + return res; + } + return 0; +} + +quint32 QHotkeyPrivateX11::nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok) +{ + quint32 nMods = 0; + if (modifiers & Qt::ShiftModifier) + nMods |= ShiftMask; + if (modifiers & Qt::ControlModifier) + nMods |= ControlMask; + if (modifiers & Qt::AltModifier) + nMods |= Mod1Mask; + if (modifiers & Qt::MetaModifier) + nMods |= Mod4Mask; + ok = true; + return nMods; +} + +bool QHotkeyPrivateX11::registerShortcut(QHotkey::NativeShortcut shortcut) +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) + const QNativeInterface::QX11Application *x11Interface = qGuiApp->nativeInterface(); + Display *display = x11Interface->display(); +#else + const bool x11Interface = QX11Info::isPlatformX11(); + Display *display = QX11Info::display(); +#endif + + if(!display || !x11Interface) + return false; + + HotkeyErrorHandler errorHandler; + for(quint32 specialMod : QHotkeyPrivateX11::specialModifiers) { + XGrabKey(display, + shortcut.key, + shortcut.modifier | specialMod, + DefaultRootWindow(display), + True, + GrabModeAsync, + GrabModeAsync); + } + XSync(display, False); + + if(errorHandler.hasError) { + error = errorHandler.errorString; + this->unregisterShortcut(shortcut); + return false; + } + return true; +} + +bool QHotkeyPrivateX11::unregisterShortcut(QHotkey::NativeShortcut shortcut) +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) + Display *display = qGuiApp->nativeInterface()->display(); +#else + Display *display = QX11Info::display(); +#endif + + if(!display) + return false; + + HotkeyErrorHandler errorHandler; + for(quint32 specialMod : QHotkeyPrivateX11::specialModifiers) { + XUngrabKey(display, + shortcut.key, + shortcut.modifier | specialMod, + XDefaultRootWindow(display)); + } + XSync(display, False); + + if(HotkeyErrorHandler::hasError) { + error = HotkeyErrorHandler::errorString; + return false; + } + return true; +} + +QString QHotkeyPrivateX11::formatX11Error(Display *display, int errorCode) +{ + char errStr[256]; + XGetErrorText(display, errorCode, errStr, 256); + return QString::fromLatin1(errStr); +} + + + +// ---------- QHotkeyPrivateX11::HotkeyErrorHandler implementation ---------- + +bool QHotkeyPrivateX11::HotkeyErrorHandler::hasError = false; +QString QHotkeyPrivateX11::HotkeyErrorHandler::errorString; + +QHotkeyPrivateX11::HotkeyErrorHandler::HotkeyErrorHandler() +{ + prevHandler = XSetErrorHandler(&HotkeyErrorHandler::handleError); +} + +QHotkeyPrivateX11::HotkeyErrorHandler::~HotkeyErrorHandler() +{ + XSetErrorHandler(prevHandler); + hasError = false; + errorString.clear(); +} + +int QHotkeyPrivateX11::HotkeyErrorHandler::handleError(Display *display, XErrorEvent *error) +{ + switch (error->error_code) { + case BadAccess: + case BadValue: + case BadWindow: + if (error->request_code == 33 || //grab key + error->request_code == 34) {// ungrab key + hasError = true; + errorString = QHotkeyPrivateX11::formatX11Error(display, error->error_code); + return 1; + } + Q_FALLTHROUGH(); + // fall through + default: + return 0; + } +} From 39f58278ff8e1af6a899bbcf7f43e1e992e4af7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Thu, 16 May 2024 01:26:59 +0800 Subject: [PATCH 32/35] update --- src/CMakeLists.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 965227ac..591a0eb9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -197,6 +197,24 @@ target_link_libraries(${PROJECT_NAME} PUBLIC Qt${QT_VERSION_MAJOR}::Quick Qt${QT_VERSION_MAJOR}::Qml ) +if(APPLE) + find_library(CARBON_LIBRARY Carbon) + target_link_libraries(${PROJECT_NAME} PRIVATE ${CARBON_LIBRARY}) +elseif(WIN32) + target_link_libraries(${PROJECT_NAME} PRIVATE user32) +elseif(UNIX) + if(QT_VERSION_MAJOR STREQUAL "6") + if(QT_VERSION VERSION_LESS "6.2.0") + message(FATAL_ERROR "Qt 6.2.0 or greater is required when using Qt6") + endif() + else() + if(QT_VERSION_MAJOR LESS "6") + find_package(Qt5 REQUIRED COMPONENTS X11Extras) + target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::X11Extras) + endif() + endif() + target_link_libraries(${PROJECT_NAME} PRIVATE X11) +endif() if ((${QT_VERSION_MAJOR} LESS_EQUAL 6) AND (CMAKE_BUILD_TYPE MATCHES "Release")) find_program(QML_PLUGIN_DUMP NAMES qmlplugindump) From 7a67ccc1e12a45bfdd5f73d00e02d39f309c93ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Fri, 17 May 2024 21:19:10 +0800 Subject: [PATCH 33/35] update --- example/example_en_US.ts | 44 +++++++++---------- example/example_zh_CN.ts | 44 +++++++++---------- example/qml/page/T_ShortcutPicker.qml | 2 - example/qml/page/T_TableView.qml | 4 +- src/FluFrameless.cpp | 22 +++------- src/FluentUI.cpp | 27 +++++++++--- .../imports/FluentUI/Controls/FluRouter.qml | 12 ++--- .../imports/FluentUI/Controls/FluWindow.qml | 3 ++ .../imports/FluentUI/Controls/FluRouter.qml | 16 +++---- .../imports/FluentUI/Controls/FluWindow.qml | 3 ++ 10 files changed, 92 insertions(+), 85 deletions(-) diff --git a/example/example_en_US.ts b/example/example_en_US.ts index 8eb7b8d9..109d1894 100644 --- a/example/example_en_US.ts +++ b/example/example_en_US.ts @@ -2043,108 +2043,108 @@ Some contents... - + Modify the column name - + Cancel - + OK - + Search - - + + Name - + Delete - + Edit - + Select All - + Age - + Clear All - + Delete Selection - + Add a row of Data - + Insert a Row - + Focus not acquired: Please click any item in the form as the target for insertion! - + Avatar - + Address - + Nickname - + Long String - + Options - + <Previous - + Next> diff --git a/example/example_zh_CN.ts b/example/example_zh_CN.ts index bd23e4ad..3776583c 100644 --- a/example/example_zh_CN.ts +++ b/example/example_zh_CN.ts @@ -2193,108 +2193,108 @@ Some contents... 表格 - + Modify the column name 修改列名 - + Cancel 取消 - + OK 确定 - + Search 搜索 - - + + Name 名称 - + Delete 删除 - + Edit 编辑 - + Select All 全选 - + Age 年龄 - + Clear All 清除所有 - + Insert a Row 插入一行 - + Focus not acquired: Please click any item in the form as the target for insertion! 焦点未获取:请点击表格中的任意一项,作为插入的靶点! - + Avatar 头像 - + Address 地址 - + Nickname 昵称 - + Long String 长字符串 - + Options 操作 - + <Previous <上一页 - + Next> 下一页> - + Delete Selection 删除选中 - + Add a row of Data 添加一行数据 diff --git a/example/qml/page/T_ShortcutPicker.qml b/example/qml/page/T_ShortcutPicker.qml index 8e5f8f0a..fce03284 100644 --- a/example/qml/page/T_ShortcutPicker.qml +++ b/example/qml/page/T_ShortcutPicker.qml @@ -40,5 +40,3 @@ FluScrollablePage{ } } - - diff --git a/example/qml/page/T_TableView.qml b/example/qml/page/T_TableView.qml index 9f696693..dc4ae88a 100644 --- a/example/qml/page/T_TableView.qml +++ b/example/qml/page/T_TableView.qml @@ -11,7 +11,6 @@ FluContentPage{ title: qsTr("TableView") signal checkBoxChanged - property var dataSource : [] property int sortType: 0 property bool selectedAll: true property string nameKeyword: "" @@ -621,7 +620,6 @@ FluContentPage{ for(var i=0;isetMaximumSize(QSize(w, h)); window()->setMinimumSize(QSize(w, h)); - }else{ - window()->setMinimumHeight(window()->minimumHeight()+appBarHeight); - window()->setMaximumHeight(window()->maximumHeight()+appBarHeight); + } else { + window()->setMinimumHeight(window()->minimumHeight() + appBarHeight); + window()->setMaximumHeight(window()->maximumHeight() + appBarHeight); } window()->resize(QSize(w, h)); connect(this, &FluFrameless::topmostChanged, this, [this] { @@ -192,8 +192,8 @@ void FluFrameless::componentComplete() { bool isMaximum = ::IsZoomed(hwnd); if (isMaximum) { auto geometry = window()->screen()->geometry(); - auto offsetX = qAbs(geometry.left()-originalLeft); - auto offsetY = qAbs(geometry.top()-originalTop); + auto offsetX = qAbs(geometry.left() - originalLeft); + auto offsetY = qAbs(geometry.top() - originalTop); clientRect->top = originalTop + offsetY; clientRect->bottom = originalBottom - offsetY; clientRect->left = originalLeft + offsetX; @@ -273,16 +273,8 @@ void FluFrameless::componentComplete() { return true; #endif } else if (uMsg == WM_NCACTIVATE) { -#if (QT_VERSION == QT_VERSION_CHECK(6, 5, 3) || QT_VERSION == QT_VERSION_CHECK(6, 6, 0)) *result = TRUE; return true; -#else - if (isCompositionEnabled()) { - return false; - } - *result = TRUE; - return true; -#endif } else if (uMsg == WM_GETMINMAXINFO) { #if (QT_VERSION == QT_VERSION_CHECK(6, 5, 3) || QT_VERSION == QT_VERSION_CHECK(6, 6, 0)) auto *minmaxInfo = reinterpret_cast(lParam); @@ -406,13 +398,13 @@ bool FluFrameless::_hitMaximizeButton() { } void FluFrameless::_setMaximizePressed(bool val) { - if(_maximizeButton){ + if (_maximizeButton) { _maximizeButton->setProperty("down", val); } } void FluFrameless::_setMaximizeHovered(bool val) { - if(_maximizeButton){ + if (_maximizeButton) { _maximizeButton->setProperty("hover", val); } } diff --git a/src/FluentUI.cpp b/src/FluentUI.cpp index 7646fbe4..aa0842a2 100644 --- a/src/FluentUI.cpp +++ b/src/FluentUI.cpp @@ -150,20 +150,35 @@ void FluentUI::registerTypes(const char *uri) const { qmlRegisterUncreatableMetaObject(FluTimelineType::staticMetaObject, uri, major, minor, "FluTimelineType", "Access to enums & flags only"); qmlRegisterUncreatableMetaObject(FluSheetType::staticMetaObject, uri, major, minor, "FluSheetType", "Access to enums & flags only"); + qmlRegisterSingletonType(uri, major, minor, "FluApp", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QJSValue { + Q_UNUSED(engine) + return scriptEngine->newQObject(FluApp::getInstance()); + }); + qmlRegisterSingletonType(uri, major, minor, "FluColors", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QJSValue { + Q_UNUSED(engine) + return scriptEngine->newQObject(FluColors::getInstance()); + }); + qmlRegisterSingletonType(uri, major, minor, "FluTheme", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QJSValue { + Q_UNUSED(engine) + return scriptEngine->newQObject(FluTheme::getInstance()); + }); + qmlRegisterSingletonType(uri, major, minor, "FluTools", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QJSValue { + Q_UNUSED(engine) + return scriptEngine->newQObject(FluTools::getInstance()); + }); + qmlRegisterSingletonType(uri, major, minor, "FluTextStyle", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QJSValue { + Q_UNUSED(engine) + return scriptEngine->newQObject(FluTextStyle::getInstance()); + }); // qmlRegisterSingletonInstance(uri, major, minor, "FluApp", FluApp::getInstance()); // qmlRegisterSingletonInstance(uri, major, minor, "FluColors", FluColors::getInstance()); // qmlRegisterSingletonInstance(uri, major, minor, "FluTheme", FluTheme::getInstance()); // qmlRegisterSingletonInstance(uri, major, minor, "FluTools", FluTools::getInstance()); // qmlRegisterSingletonInstance(uri, major, minor, "FluTextStyle", FluTextStyle::getInstance()); - qmlRegisterModule(uri, major, minor); #endif } void FluentUI::initializeEngine(QQmlEngine *engine, [[maybe_unused]] const char *uri) { - engine->rootContext()->setContextProperty("FluApp", FluApp::getInstance()); - engine->rootContext()->setContextProperty("FluColors", FluColors::getInstance()); - engine->rootContext()->setContextProperty("FluTheme", FluTheme::getInstance()); - engine->rootContext()->setContextProperty("FluTools", FluTools::getInstance()); - engine->rootContext()->setContextProperty("FluTextStyle", FluTextStyle::getInstance()); + Q_UNUSED(engine) } diff --git a/src/Qt5/imports/FluentUI/Controls/FluRouter.qml b/src/Qt5/imports/FluentUI/Controls/FluRouter.qml index 1687fd98..7ed2e551 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluRouter.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluRouter.qml @@ -10,19 +10,19 @@ QtObject { windows.push(window) } } - function removeWindow(window) { - if(!window.transientParent){ - var index = windows.indexOf(window) + function removeWindow(win) { + if(!win.transientParent){ + var index = windows.indexOf(win) if (index !== -1) { windows.splice(index, 1) - FluTools.deleteLater(window) + win.deleteLater() } } } function exit(retCode){ for(var i =0 ;i< windows.length; i++){ - var item = windows[i] - FluTools.deleteLater(item) + var win = windows[i] + win.deleteLater() } windows = [] Qt.exit(retCode) diff --git a/src/Qt5/imports/FluentUI/Controls/FluWindow.qml b/src/Qt5/imports/FluentUI/Controls/FluWindow.qml index 036e82e2..1905e5d3 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluWindow.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluWindow.qml @@ -353,4 +353,7 @@ Window { function setHitTestVisible(val){ frameless.setHitTestVisible(val) } + function deleteLater(){ + FluTools.deleteLater(window) + } } diff --git a/src/Qt6/imports/FluentUI/Controls/FluRouter.qml b/src/Qt6/imports/FluentUI/Controls/FluRouter.qml index a9be78fb..f1e0ee86 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluRouter.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluRouter.qml @@ -11,19 +11,19 @@ QtObject { windows.push(window) } } - function removeWindow(window) { - if(!window.transientParent){ - var index = windows.indexOf(window) + function removeWindow(win) { + if(!win.transientParent){ + var index = windows.indexOf(win) if (index !== -1) { windows.splice(index, 1) - FluTools.deleteLater(window) + win.deleteLater() } } } function exit(retCode){ for(var i =0 ;i< windows.length; i++){ - var item = windows[i] - FluTools.deleteLater(item) + var win = windows[i] + win.deleteLater() } windows = [] Qt.exit(retCode) @@ -56,9 +56,7 @@ QtObject { var launchMode = win.launchMode if(launchMode === 1){ win.argument = argument - if(!win.visible){ - win.visible = true - } + win.show() win.raise() win.requestActivate() return diff --git a/src/Qt6/imports/FluentUI/Controls/FluWindow.qml b/src/Qt6/imports/FluentUI/Controls/FluWindow.qml index d2c8fa90..a760cb21 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluWindow.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluWindow.qml @@ -352,4 +352,7 @@ Window { function setHitTestVisible(val){ frameless.setHitTestVisible(val) } + function deleteLater(){ + FluTools.deleteLater(window) + } } From a27554bfb217f6cd9aa9167469cfc7dc82109594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Fri, 17 May 2024 21:27:41 +0800 Subject: [PATCH 34/35] update --- src/FluApp.cpp | 2 +- src/FluentIconDef.h | 6 +++--- src/FluentUI.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/FluApp.cpp b/src/FluApp.cpp index a789365a..da3ab8d6 100644 --- a/src/FluApp.cpp +++ b/src/FluApp.cpp @@ -34,7 +34,7 @@ void FluApp::init(QObject *launcher, QLocale locale) { [[maybe_unused]] QJsonArray FluApp::iconData(const QString &keyword) { QJsonArray arr; - QMetaEnum enumType = Fluent_Icons::staticMetaObject.enumerator(Fluent_Icons::staticMetaObject.indexOfEnumerator("Fluent_IconType")); + QMetaEnum enumType = FluentIcons::staticMetaObject.enumerator(FluentIcons::staticMetaObject.indexOfEnumerator("Type")); for (int i = 0; i <= enumType.keyCount() - 1; ++i) { QString name = enumType.key(i); int icon = enumType.value(i); diff --git a/src/FluentIconDef.h b/src/FluentIconDef.h index 300d23bd..5fd61afd 100644 --- a/src/FluentIconDef.h +++ b/src/FluentIconDef.h @@ -3,9 +3,9 @@ #include #include -namespace Fluent_Icons { +namespace FluentIcons { Q_NAMESPACE - enum class Fluent_IconType { + enum class Type { GlobalNavButton = 0xe700, Wifi = 0xe701, Bluetooth = 0xe702, @@ -1411,7 +1411,7 @@ namespace Fluent_Icons { ClickedOutLoudSolidBold = 0xf8b3 }; - Q_ENUM_NS(Fluent_IconType) + Q_ENUM_NS(Type) QML_NAMED_ELEMENT(FluentIcons) } diff --git a/src/FluentUI.cpp b/src/FluentUI.cpp index aa0842a2..28a97aca 100644 --- a/src/FluentUI.cpp +++ b/src/FluentUI.cpp @@ -136,7 +136,7 @@ void FluentUI::registerTypes(const char *uri) const { qmlRegisterSingletonType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluRouter.qml"), uri, major, minor, "FluRouter"); qmlRegisterSingletonType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluEventBus.qml"), uri, major, minor, "FluEventBus"); - qmlRegisterUncreatableMetaObject(Fluent_Icons::staticMetaObject, uri, major, minor, "FluentIcons", "Access to enums & flags only"); + qmlRegisterUncreatableMetaObject(FluentIcons::staticMetaObject, uri, major, minor, "FluentIcons", "Access to enums & flags only"); qmlRegisterUncreatableMetaObject(FluThemeType::staticMetaObject, uri, major, minor, "FluThemeType", "Access to enums & flags only"); qmlRegisterUncreatableMetaObject(FluPageType::staticMetaObject, uri, major, minor, "FluPageType", "Access to enums & flags only"); qmlRegisterUncreatableMetaObject(FluWindowType::staticMetaObject, uri, major, minor, "FluWindowType", "Access to enums & flags only"); From 5cfd9824a8c194deaf2802569b154ea9feb1b630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Sun, 19 May 2024 13:40:36 +0800 Subject: [PATCH 35/35] update --- .../imports/FluentUI/Controls/FluInfoBar.qml | 183 ++++++++--------- .../FluentUI/Controls/FluNavigationView.qml | 17 +- .../imports/FluentUI/Controls/FluInfoBar.qml | 189 ++++++++---------- .../FluentUI/Controls/FluNavigationView.qml | 17 +- 4 files changed, 183 insertions(+), 223 deletions(-) diff --git a/src/Qt5/imports/FluentUI/Controls/FluInfoBar.qml b/src/Qt5/imports/FluentUI/Controls/FluInfoBar.qml index ecaaa37d..3177a8aa 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluInfoBar.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluInfoBar.qml @@ -3,45 +3,40 @@ import QtQuick.Controls 2.15 import FluentUI 1.0 FluObject { - property var root; + property var root property int layoutY: 75 id:control FluObject{ id:mcontrol - property string const_success: "success"; - property string const_info: "info"; - property string const_warning: "warning"; - property string const_error: "error"; - property int maxWidth: 300; - property var screenLayout: null; + property string const_success: "success" + property string const_info: "info" + property string const_warning: "warning" + property string const_error: "error" + property int maxWidth: 300 + property var screenLayout: null function create(type,text,duration,moremsg){ if(screenLayout){ - var last = screenLayout.getLastloader(); + var last = screenLayout.getLastloader() if(last.type === type && last.text === text && moremsg === last.moremsg){ last.duration = duration - if (duration > 0) last.restart(); - return last; + if (duration > 0) last.restart() + return last } } - initScreenLayout(); - return contentComponent.createObject(screenLayout,{ - type:type, - text:text, - duration:duration, - moremsg:moremsg, - }); + initScreenLayout() + return contentComponent.createObject(screenLayout,{type:type,text:text,duration:duration,moremsg:moremsg,}) } function createCustom(itemcomponent,duration){ - initScreenLayout(); + initScreenLayout() if(itemcomponent){ - return contentComponent.createObject(screenLayout,{itemcomponent:itemcomponent,duration:duration}); + return contentComponent.createObject(screenLayout,{itemcomponent:itemcomponent,duration:duration}) } } function initScreenLayout(){ if(screenLayout == null){ - screenLayout = screenlayoutComponent.createObject(root); - screenLayout.y = control.layoutY; - screenLayout.z = 100000; + screenLayout = screenlayoutComponent.createObject(root) + screenLayout.y = control.layoutY + screenLayout.z = 100000 } } Component{ @@ -58,44 +53,44 @@ FluObject { duration: FluTheme.animationEnabled ? 333 : 0 } } - onChildrenChanged: if(children.length === 0) destroy(); + onChildrenChanged: if(children.length === 0) destroy() function getLastloader(){ if(children.length > 0){ - return children[children.length - 1]; + return children[children.length - 1] } - return null; + return null } } } Component{ id:contentComponent Item{ - id:content; + id:content property int duration: 1500 property var itemcomponent property string type property string text property string moremsg - width: parent.width; - height: loader.height; + width: parent.width + height: loader.height function close(){ - content.destroy(); + content.destroy() } function restart(){ - delayTimer.restart(); + delayTimer.restart() } Timer { id:delayTimer - interval: duration; - running: duration > 0; + interval: duration + running: duration > 0 repeat: duration > 0 - onTriggered: content.close(); + onTriggered: content.close() } FluLoader{ - id:loader; - x:(parent.width - width) / 2; - property var _super: content; - scale: item ? 1 : 0; + id:loader + x:(parent.width - width) / 2 + property var _super: content + scale: item ? 1 : 0 asynchronous: true Behavior on scale { enabled: FluTheme.animationEnabled @@ -104,30 +99,30 @@ FluObject { duration: 167 } } - sourceComponent:itemcomponent ? itemcomponent : mcontrol.fluent_sytle; + sourceComponent:itemcomponent ? itemcomponent : mcontrol.fluent_sytle } } } property Component fluent_sytle: Rectangle{ - width: rowlayout.width + (btn_close.visible ? 30 : 48); - height: rowlayout.height + 20; + width: rowlayout.width + (btn_close.visible ? 30 : 48) + height: rowlayout.height + 20 color: { if(FluTheme.dark){ switch(_super.type){ - case mcontrol.const_success: return Qt.rgba(57/255,61/255,27/255,1); - case mcontrol.const_warning: return Qt.rgba(67/255,53/255,25/255,1); - case mcontrol.const_info: return Qt.rgba(39/255,39/255,39/255,1); - case mcontrol.const_error: return Qt.rgba(68/255,39/255,38/255,1); + case mcontrol.const_success: return Qt.rgba(57/255,61/255,27/255,1) + case mcontrol.const_warning: return Qt.rgba(67/255,53/255,25/255,1) + case mcontrol.const_info: return Qt.rgba(39/255,39/255,39/255,1) + case mcontrol.const_error: return Qt.rgba(68/255,39/255,38/255,1) } - return Qt.rgba(255,255,255,1) + return Qt.rgba(1,1,1,1) }else{ switch(_super.type){ - case mcontrol.const_success: return "#dff6dd"; - case mcontrol.const_warning: return "#fff4ce"; - case mcontrol.const_info: return "#f4f4f4"; - case mcontrol.const_error: return "#fde7e9"; + case mcontrol.const_success: return Qt.rgba(223/255,246/255,221/255,1) + case mcontrol.const_warning: return Qt.rgba(255/255,244/255,206/255,1) + case mcontrol.const_info: return Qt.rgba(244/255,244/255,244/255,1) + case mcontrol.const_error: return Qt.rgba(253/255,231/255,233/255,1) } - return "#FFFFFF" + return Qt.rgba(1,1,1,1) } } FluShadow{ @@ -138,34 +133,34 @@ FluObject { border.color: { if(FluTheme.dark){ switch(_super.type){ - case mcontrol.const_success: return Qt.rgba(56/255,61/255,27/255,1); - case mcontrol.const_warning: return Qt.rgba(66/255,53/255,25/255,1); - case mcontrol.const_info: return Qt.rgba(38/255,39/255,39/255,1); - case mcontrol.const_error: return Qt.rgba(67/255,39/255,38/255,1); + case mcontrol.const_success: return Qt.rgba(56/255,61/255,27/255,1) + case mcontrol.const_warning: return Qt.rgba(66/255,53/255,25/255,1) + case mcontrol.const_info: return Qt.rgba(38/255,39/255,39/255,1) + case mcontrol.const_error: return Qt.rgba(67/255,39/255,38/255,1) } - return "#FFFFFF" + return Qt.rgba(1,1,1,1) }else{ switch(_super.type){ - case mcontrol.const_success: return "#d2e8d0"; - case mcontrol.const_warning: return "#f0e6c2"; - case mcontrol.const_info: return "#e6e6e6"; - case mcontrol.const_error: return "#eed9db"; + case mcontrol.const_success: return Qt.rgba(210/255,232/255,208/255,1) + case mcontrol.const_warning: return Qt.rgba(240/255,230/255,194/255,1) + case mcontrol.const_info: return Qt.rgba(230/255,230/255,230/255,1) + case mcontrol.const_error: return Qt.rgba(238/255,217/255,219/255,1) } - return "#FFFFFF" + return Qt.rgba(1,1,1,1) } } Row{ id:rowlayout - x:20; - y:(parent.height - height) / 2; + x:20 + y:(parent.height - height) / 2 spacing: 10 FluIcon{ iconSource:{ switch(_super.type){ - case mcontrol.const_success: return FluentIcons.CompletedSolid; - case mcontrol.const_warning: return FluentIcons.InfoSolid; - case mcontrol.const_info: return FluentIcons.InfoSolid; - case mcontrol.const_error: return FluentIcons.StatusErrorFull; + case mcontrol.const_success: return FluentIcons.CompletedSolid + case mcontrol.const_warning: return FluentIcons.InfoSolid + case mcontrol.const_info: return FluentIcons.InfoSolid + case mcontrol.const_error: return FluentIcons.StatusErrorFull }FluentIcons.StatusErrorFull return FluentIcons.FA_info_circle } @@ -173,20 +168,20 @@ FluObject { iconColor: { if(FluTheme.dark){ switch(_super.type){ - case mcontrol.const_success: return Qt.rgba(108/255,203/255,95/255,1); - case mcontrol.const_warning: return Qt.rgba(252/255,225/255,0/255,1); - case mcontrol.const_info: return FluTheme.primaryColor; - case mcontrol.const_error: return Qt.rgba(255/255,153/255,164/255,1); + case mcontrol.const_success: return Qt.rgba(108/255,203/255,95/255,1) + case mcontrol.const_warning: return Qt.rgba(252/255,225/255,0/255,1) + case mcontrol.const_info: return FluTheme.primaryColor + case mcontrol.const_error: return Qt.rgba(255/255,153/255,164/255,1) } - return "#FFFFFF" + return Qt.rgba(1,1,1,1) }else{ switch(_super.type){ - case mcontrol.const_success: return "#0f7b0f"; - case mcontrol.const_warning: return "#9d5d00"; - case mcontrol.const_info: return "#0066b4"; - case mcontrol.const_error: return "#c42b1c"; + case mcontrol.const_success: return Qt.rgba(15/255,123/255,15/255,1) + case mcontrol.const_warning: return Qt.rgba(157/255,93/255,0/255,1) + case mcontrol.const_info: return Qt.rgba(0/255,102/255,180/255,1) + case mcontrol.const_error: return Qt.rgba(196/255,43/255,28/255,1) } - return "#FFFFFF" + return Qt.rgba(1,1,1,1) } } } @@ -211,46 +206,32 @@ FluObject { id:btn_close iconSource: FluentIcons.ChromeClose iconSize: 10 - y:5 + verticalPadding: 0 + horizontalPadding: 0 + width: 30 + height: 20 visible: _super.duration<=0 - iconColor: { - if(FluTheme.dark){ - switch(_super.type){ - case mcontrol.const_success: return Qt.rgba(108/255,203/255,95/255,1); - case mcontrol.const_warning: return Qt.rgba(252/255,225/255,0/255,1); - case mcontrol.const_info: return FluTheme.primaryColor; - case mcontrol.const_error: return Qt.rgba(255/255,153/255,164/255,1); - } - return "#FFFFFF" - }else{ - switch(_super.type){ - case mcontrol.const_success: return "#0f7b0f"; - case mcontrol.const_warning: return "#9d5d00"; - case mcontrol.const_info: return "#0066b4"; - case mcontrol.const_error: return "#c42b1c"; - } - return "#FFFFFF" - } - } + anchors.verticalCenter: parent.verticalCenter + iconColor: FluTheme.dark ? Qt.rgba(222/255,222/255,222/255,1) : Qt.rgba(97/255,97/255,97/255,1) onClicked: _super.close() } } } } function showSuccess(text,duration=1000,moremsg){ - return mcontrol.create(mcontrol.const_success,text,duration,moremsg ? moremsg : ""); + return mcontrol.create(mcontrol.const_success,text,duration,moremsg ? moremsg : "") } function showInfo(text,duration=1000,moremsg){ - return mcontrol.create(mcontrol.const_info,text,duration,moremsg ? moremsg : ""); + return mcontrol.create(mcontrol.const_info,text,duration,moremsg ? moremsg : "") } function showWarning(text,duration=1000,moremsg){ - return mcontrol.create(mcontrol.const_warning,text,duration,moremsg ? moremsg : ""); + return mcontrol.create(mcontrol.const_warning,text,duration,moremsg ? moremsg : "") } function showError(text,duration=1000,moremsg){ - return mcontrol.create(mcontrol.const_error,text,duration,moremsg ? moremsg : ""); + return mcontrol.create(mcontrol.const_error,text,duration,moremsg ? moremsg : "") } function showCustom(itemcomponent,duration=1000){ - return mcontrol.createCustom(itemcomponent,duration); + return mcontrol.createCustom(itemcomponent,duration) } function clearAllInfo(){ if(mcontrol.screenLayout != null) { diff --git a/src/Qt5/imports/FluentUI/Controls/FluNavigationView.qml b/src/Qt5/imports/FluentUI/Controls/FluNavigationView.qml index 7ea83d06..3d06ad29 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluNavigationView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluNavigationView.qml @@ -1120,7 +1120,8 @@ Item { } padding: 0 focus: true - contentItem: Item{ + contentItem: FluClip{ + radius: [5,5,5,5] ListView{ id:list_view anchors.fill: parent @@ -1145,7 +1146,6 @@ Item { visible: item_button.activeFocus radius:4 } - FluLoader{ id:item_dot_loader anchors{ @@ -1160,7 +1160,6 @@ Item { return undefined } } - } contentItem: FluText{ text:modelData.title @@ -1187,13 +1186,13 @@ Item { } } } - background: FluRectangle{ + background: Rectangle{ implicitWidth: 180 - radius: [4,4,4,4] - FluShadow{ - radius: 4 - } - color: FluTheme.dark ? Qt.rgba(51/255,48/255,48/255,1) : Qt.rgba(248/255,250/255,253/255,1) + color:FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(252/255,252/255,252/255,1) + border.color: FluTheme.dark ? Qt.rgba(26/255,26/255,26/255,1) : Qt.rgba(191/255,191/255,191/255,1) + border.width: 1 + radius: 5 + FluShadow{} } function showPopup(pos,height,model){ background.implicitHeight = height diff --git a/src/Qt6/imports/FluentUI/Controls/FluInfoBar.qml b/src/Qt6/imports/FluentUI/Controls/FluInfoBar.qml index ecaaa37d..6fac2e08 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluInfoBar.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluInfoBar.qml @@ -1,47 +1,42 @@ -import QtQuick 2.15 -import QtQuick.Controls 2.15 -import FluentUI 1.0 +import QtQuick +import QtQuick.Controls +import FluentUI FluObject { - property var root; + property var root property int layoutY: 75 id:control FluObject{ id:mcontrol - property string const_success: "success"; - property string const_info: "info"; - property string const_warning: "warning"; - property string const_error: "error"; - property int maxWidth: 300; - property var screenLayout: null; + property string const_success: "success" + property string const_info: "info" + property string const_warning: "warning" + property string const_error: "error" + property int maxWidth: 300 + property var screenLayout: null function create(type,text,duration,moremsg){ if(screenLayout){ - var last = screenLayout.getLastloader(); + var last = screenLayout.getLastloader() if(last.type === type && last.text === text && moremsg === last.moremsg){ last.duration = duration - if (duration > 0) last.restart(); - return last; + if (duration > 0) last.restart() + return last } } - initScreenLayout(); - return contentComponent.createObject(screenLayout,{ - type:type, - text:text, - duration:duration, - moremsg:moremsg, - }); + initScreenLayout() + return contentComponent.createObject(screenLayout,{type:type,text:text,duration:duration,moremsg:moremsg,}) } function createCustom(itemcomponent,duration){ - initScreenLayout(); + initScreenLayout() if(itemcomponent){ - return contentComponent.createObject(screenLayout,{itemcomponent:itemcomponent,duration:duration}); + return contentComponent.createObject(screenLayout,{itemcomponent:itemcomponent,duration:duration}) } } function initScreenLayout(){ if(screenLayout == null){ - screenLayout = screenlayoutComponent.createObject(root); - screenLayout.y = control.layoutY; - screenLayout.z = 100000; + screenLayout = screenlayoutComponent.createObject(root) + screenLayout.y = control.layoutY + screenLayout.z = 100000 } } Component{ @@ -58,44 +53,44 @@ FluObject { duration: FluTheme.animationEnabled ? 333 : 0 } } - onChildrenChanged: if(children.length === 0) destroy(); + onChildrenChanged: if(children.length === 0) destroy() function getLastloader(){ if(children.length > 0){ - return children[children.length - 1]; + return children[children.length - 1] } - return null; + return null } } } Component{ id:contentComponent Item{ - id:content; + id:content property int duration: 1500 property var itemcomponent property string type property string text property string moremsg - width: parent.width; - height: loader.height; + width: parent.width + height: loader.height function close(){ - content.destroy(); + content.destroy() } function restart(){ - delayTimer.restart(); + delayTimer.restart() } Timer { id:delayTimer - interval: duration; - running: duration > 0; + interval: duration + running: duration > 0 repeat: duration > 0 - onTriggered: content.close(); + onTriggered: content.close() } FluLoader{ - id:loader; - x:(parent.width - width) / 2; - property var _super: content; - scale: item ? 1 : 0; + id:loader + x:(parent.width - width) / 2 + property var _super: content + scale: item ? 1 : 0 asynchronous: true Behavior on scale { enabled: FluTheme.animationEnabled @@ -104,30 +99,30 @@ FluObject { duration: 167 } } - sourceComponent:itemcomponent ? itemcomponent : mcontrol.fluent_sytle; + sourceComponent:itemcomponent ? itemcomponent : mcontrol.fluent_sytle } } } property Component fluent_sytle: Rectangle{ - width: rowlayout.width + (btn_close.visible ? 30 : 48); - height: rowlayout.height + 20; + width: rowlayout.width + (btn_close.visible ? 30 : 48) + height: rowlayout.height + 20 color: { if(FluTheme.dark){ switch(_super.type){ - case mcontrol.const_success: return Qt.rgba(57/255,61/255,27/255,1); - case mcontrol.const_warning: return Qt.rgba(67/255,53/255,25/255,1); - case mcontrol.const_info: return Qt.rgba(39/255,39/255,39/255,1); - case mcontrol.const_error: return Qt.rgba(68/255,39/255,38/255,1); + case mcontrol.const_success: return Qt.rgba(57/255,61/255,27/255,1) + case mcontrol.const_warning: return Qt.rgba(67/255,53/255,25/255,1) + case mcontrol.const_info: return Qt.rgba(39/255,39/255,39/255,1) + case mcontrol.const_error: return Qt.rgba(68/255,39/255,38/255,1) } - return Qt.rgba(255,255,255,1) + return Qt.rgba(1,1,1,1) }else{ switch(_super.type){ - case mcontrol.const_success: return "#dff6dd"; - case mcontrol.const_warning: return "#fff4ce"; - case mcontrol.const_info: return "#f4f4f4"; - case mcontrol.const_error: return "#fde7e9"; + case mcontrol.const_success: return Qt.rgba(223/255,246/255,221/255,1) + case mcontrol.const_warning: return Qt.rgba(255/255,244/255,206/255,1) + case mcontrol.const_info: return Qt.rgba(244/255,244/255,244/255,1) + case mcontrol.const_error: return Qt.rgba(253/255,231/255,233/255,1) } - return "#FFFFFF" + return Qt.rgba(1,1,1,1) } } FluShadow{ @@ -138,34 +133,34 @@ FluObject { border.color: { if(FluTheme.dark){ switch(_super.type){ - case mcontrol.const_success: return Qt.rgba(56/255,61/255,27/255,1); - case mcontrol.const_warning: return Qt.rgba(66/255,53/255,25/255,1); - case mcontrol.const_info: return Qt.rgba(38/255,39/255,39/255,1); - case mcontrol.const_error: return Qt.rgba(67/255,39/255,38/255,1); + case mcontrol.const_success: return Qt.rgba(56/255,61/255,27/255,1) + case mcontrol.const_warning: return Qt.rgba(66/255,53/255,25/255,1) + case mcontrol.const_info: return Qt.rgba(38/255,39/255,39/255,1) + case mcontrol.const_error: return Qt.rgba(67/255,39/255,38/255,1) } - return "#FFFFFF" + return Qt.rgba(1,1,1,1) }else{ switch(_super.type){ - case mcontrol.const_success: return "#d2e8d0"; - case mcontrol.const_warning: return "#f0e6c2"; - case mcontrol.const_info: return "#e6e6e6"; - case mcontrol.const_error: return "#eed9db"; + case mcontrol.const_success: return Qt.rgba(210/255,232/255,208/255,1) + case mcontrol.const_warning: return Qt.rgba(240/255,230/255,194/255,1) + case mcontrol.const_info: return Qt.rgba(230/255,230/255,230/255,1) + case mcontrol.const_error: return Qt.rgba(238/255,217/255,219/255,1) } - return "#FFFFFF" + return Qt.rgba(1,1,1,1) } } Row{ id:rowlayout - x:20; - y:(parent.height - height) / 2; + x:20 + y:(parent.height - height) / 2 spacing: 10 FluIcon{ iconSource:{ switch(_super.type){ - case mcontrol.const_success: return FluentIcons.CompletedSolid; - case mcontrol.const_warning: return FluentIcons.InfoSolid; - case mcontrol.const_info: return FluentIcons.InfoSolid; - case mcontrol.const_error: return FluentIcons.StatusErrorFull; + case mcontrol.const_success: return FluentIcons.CompletedSolid + case mcontrol.const_warning: return FluentIcons.InfoSolid + case mcontrol.const_info: return FluentIcons.InfoSolid + case mcontrol.const_error: return FluentIcons.StatusErrorFull }FluentIcons.StatusErrorFull return FluentIcons.FA_info_circle } @@ -173,20 +168,20 @@ FluObject { iconColor: { if(FluTheme.dark){ switch(_super.type){ - case mcontrol.const_success: return Qt.rgba(108/255,203/255,95/255,1); - case mcontrol.const_warning: return Qt.rgba(252/255,225/255,0/255,1); - case mcontrol.const_info: return FluTheme.primaryColor; - case mcontrol.const_error: return Qt.rgba(255/255,153/255,164/255,1); + case mcontrol.const_success: return Qt.rgba(108/255,203/255,95/255,1) + case mcontrol.const_warning: return Qt.rgba(252/255,225/255,0/255,1) + case mcontrol.const_info: return FluTheme.primaryColor + case mcontrol.const_error: return Qt.rgba(255/255,153/255,164/255,1) } - return "#FFFFFF" + return Qt.rgba(1,1,1,1) }else{ switch(_super.type){ - case mcontrol.const_success: return "#0f7b0f"; - case mcontrol.const_warning: return "#9d5d00"; - case mcontrol.const_info: return "#0066b4"; - case mcontrol.const_error: return "#c42b1c"; + case mcontrol.const_success: return Qt.rgba(15/255,123/255,15/255,1) + case mcontrol.const_warning: return Qt.rgba(157/255,93/255,0/255,1) + case mcontrol.const_info: return Qt.rgba(0/255,102/255,180/255,1) + case mcontrol.const_error: return Qt.rgba(196/255,43/255,28/255,1) } - return "#FFFFFF" + return Qt.rgba(1,1,1,1) } } } @@ -211,46 +206,32 @@ FluObject { id:btn_close iconSource: FluentIcons.ChromeClose iconSize: 10 - y:5 + verticalPadding: 0 + horizontalPadding: 0 + width: 30 + height: 20 visible: _super.duration<=0 - iconColor: { - if(FluTheme.dark){ - switch(_super.type){ - case mcontrol.const_success: return Qt.rgba(108/255,203/255,95/255,1); - case mcontrol.const_warning: return Qt.rgba(252/255,225/255,0/255,1); - case mcontrol.const_info: return FluTheme.primaryColor; - case mcontrol.const_error: return Qt.rgba(255/255,153/255,164/255,1); - } - return "#FFFFFF" - }else{ - switch(_super.type){ - case mcontrol.const_success: return "#0f7b0f"; - case mcontrol.const_warning: return "#9d5d00"; - case mcontrol.const_info: return "#0066b4"; - case mcontrol.const_error: return "#c42b1c"; - } - return "#FFFFFF" - } - } + anchors.verticalCenter: parent.verticalCenter + iconColor: FluTheme.dark ? Qt.rgba(222/255,222/255,222/255,1) : Qt.rgba(97/255,97/255,97/255,1) onClicked: _super.close() } } } } function showSuccess(text,duration=1000,moremsg){ - return mcontrol.create(mcontrol.const_success,text,duration,moremsg ? moremsg : ""); + return mcontrol.create(mcontrol.const_success,text,duration,moremsg ? moremsg : "") } function showInfo(text,duration=1000,moremsg){ - return mcontrol.create(mcontrol.const_info,text,duration,moremsg ? moremsg : ""); + return mcontrol.create(mcontrol.const_info,text,duration,moremsg ? moremsg : "") } function showWarning(text,duration=1000,moremsg){ - return mcontrol.create(mcontrol.const_warning,text,duration,moremsg ? moremsg : ""); + return mcontrol.create(mcontrol.const_warning,text,duration,moremsg ? moremsg : "") } function showError(text,duration=1000,moremsg){ - return mcontrol.create(mcontrol.const_error,text,duration,moremsg ? moremsg : ""); + return mcontrol.create(mcontrol.const_error,text,duration,moremsg ? moremsg : "") } function showCustom(itemcomponent,duration=1000){ - return mcontrol.createCustom(itemcomponent,duration); + return mcontrol.createCustom(itemcomponent,duration) } function clearAllInfo(){ if(mcontrol.screenLayout != null) { diff --git a/src/Qt6/imports/FluentUI/Controls/FluNavigationView.qml b/src/Qt6/imports/FluentUI/Controls/FluNavigationView.qml index ad836a3b..b9b2a229 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluNavigationView.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluNavigationView.qml @@ -1121,7 +1121,8 @@ Item { } padding: 0 focus: true - contentItem: Item{ + contentItem: FluClip{ + radius: [5,5,5,5] ListView{ id:list_view anchors.fill: parent @@ -1146,7 +1147,6 @@ Item { visible: item_button.activeFocus radius:4 } - FluLoader{ id:item_dot_loader anchors{ @@ -1161,7 +1161,6 @@ Item { return undefined } } - } contentItem: FluText{ text:modelData.title @@ -1188,13 +1187,13 @@ Item { } } } - background: FluRectangle{ + background: Rectangle{ implicitWidth: 180 - radius: [4,4,4,4] - FluShadow{ - radius: 4 - } - color: FluTheme.dark ? Qt.rgba(51/255,48/255,48/255,1) : Qt.rgba(248/255,250/255,253/255,1) + color:FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(252/255,252/255,252/255,1) + border.color: FluTheme.dark ? Qt.rgba(26/255,26/255,26/255,1) : Qt.rgba(191/255,191/255,191/255,1) + border.width: 1 + radius: 5 + FluShadow{} } function showPopup(pos,height,model){ background.implicitHeight = height