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 ceabe270..109d1894 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
@@ -1273,25 +1329,31 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
-
+
E-mail
-
+
Calendar
-
+
Contacts
RadioButton Group
+ RadioButton Group111111111111111111111111
+
+
+
+
+ Disabled
@@ -1331,12 +1393,7 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
-
- Search
-
-
-
-
+
You Copied
@@ -1362,51 +1419,82 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
-
+
Info
-
-
+
+
This is an InfoBar in the Info Style
-
+
Warning
-
+
This is an InfoBar in the Warning Style
-
+
This is an InfoBar in the Error Style
-
+
This is an InfoBar in the Success Style
-
+
InfoBar that needs to be turned off manually
-
+
Manual shutdown is supported
+
+
+ Manually close the info message box
+
+
+
+
+
+
+ close '%1'
+
+
+
+
+
+
+ show '%1
+
+
+
+
+
+
+ This is an '%1'
+
+
+
+
+ clear all info
+
+
T_LineChart
-
+
Line Chart
@@ -1704,22 +1792,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
@@ -1947,88 +2043,98 @@ 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
@@ -2042,16 +2148,6 @@ Some contents...
Next>
-
-
- Delete Selection
-
-
-
-
- Add a row of Data
-
-
T_Text
@@ -2203,12 +2299,12 @@ Some contents...
-
+
Append
-
+
clear
diff --git a/example/example_zh_CN.ts b/example/example_zh_CN.ts
index c86300be..3776583c 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
@@ -1359,27 +1415,33 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
-
+
E-mail
邮箱
-
+
Calendar
日历
-
+
Contacts
联系人
RadioButton Group
+ RadioButton Group111111111111111111111111
单选框分组
+
+
+ Disabled
+ 禁用
+
T_Home
@@ -1417,12 +1479,11 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
请输入关键字
-
Search
- 搜索
+ 搜索
-
+
You Copied
您复制
@@ -1452,46 +1513,77 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
信息栏
-
+
Info
-
-
+
+
This is an InfoBar in the Info Style
这是一个Info样式的信息栏
-
+
Warning
-
+
This is an InfoBar in the Warning Style
这是一个Warning样式的信息栏
-
+
This is an InfoBar in the Error Style
这是一个Error样式的信息栏
-
+
This is an InfoBar in the Success Style
这是一个Success样式的信息栏
-
+
InfoBar that needs to be turned off manually
需要手动关闭的信息栏
-
+
Manual shutdown is supported
支持手动关闭
+
+
+ Manually close the info message box
+
+
+
+
+
+
+ close '%1'
+
+
+
+
+
+
+ show '%1
+
+
+
+
+
+
+ This is an '%1'
+
+
+
+
+ clear all info
+
+
Loading...
加载中...
@@ -1500,7 +1592,7 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_LineChart
-
+
Line Chart
线型图
@@ -1808,22 +1900,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
@@ -1999,6 +2099,10 @@ Some contents...
ShortcutPicker
快捷键选择器
+
+ Quit
+ 退出
+
Activate the Shortcut
激活快捷键
@@ -2089,88 +2193,88 @@ 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
操作
@@ -2185,12 +2289,12 @@ Some contents...
下一页>
-
+
Delete Selection
删除选中
-
+
Add a row of Data
添加一行数据
@@ -2345,12 +2449,12 @@ Some contents...
时间轴
-
+
Append
追加
-
+
clear
清空
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/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/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_RadioButton.qml b/example/qml/page/T_RadioButton.qml
index 2f08e329..a8ab4dcb 100644
--- a/example/qml/page/T_RadioButton.qml
+++ b/example/qml/page/T_RadioButton.qml
@@ -53,21 +53,20 @@ FluScrollablePage{
Layout.topMargin: 20
FluRadioButtons{
spacing: 8
+ disabled: radio_button_switch2.checked
anchors.verticalCenter: parent.verticalCenter
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
}
+ currentIndex: 1
FluRadioButton{
- disabled: radio_button_switch2.checked
text: qsTr("Radio Button_1")
}
FluRadioButton{
- disabled: radio_button_switch2.checked
text: qsTr("Radio Button_2")
}
FluRadioButton{
- disabled: radio_button_switch2.checked
text: qsTr("Radio Button_3")
}
}
@@ -97,4 +96,106 @@ 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
+ }
+ disabled: radio_button_switch3.checked
+ orientation: Qt.Horizontal
+ currentIndex: 1
+ FluRadioButton{
+ text: qsTr("Radio Button_1")
+ }
+ FluRadioButton{
+ text: qsTr("Radio Button_2")
+ }
+ FluRadioButton{
+ 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
+ }
+ disabled: radio_button_switch4.checked
+ currentIndex: -1
+ FluCheckBox{
+ text: qsTr("Radio Button_1")
+ }
+ FluCheckBox{
+ text: qsTr("Radio Button_2")
+ }
+ FluCheckBox{
+ 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/example/qml/page/T_ShortcutPicker.qml b/example/qml/page/T_ShortcutPicker.qml
index d82edeb3..fce03284 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
@@ -26,5 +40,3 @@ FluScrollablePage{
}
}
-
-
diff --git a/example/qml/page/T_TableView.qml b/example/qml/page/T_TableView.qml
index b309c369..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: ""
@@ -244,7 +243,9 @@ FluContentPage{
clickListener: function(){
root.selectedAll = !root.selectedAll
var checked = root.selectedAll
- itemModel.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 +272,8 @@ FluContentPage{
}
Component.onCompleted: {
currentIndex=["100","300","500","1000"].findIndex((element) => element === display)
- selectAll()
+ textBox.forceActiveFocus()
+ textBox.selectAll()
}
onCommit: {
editTextChaged(editText)
@@ -293,6 +295,8 @@ FluContentPage{
});
items = result
textbox.text= String(display)
+ forceActiveFocus()
+ selectAll()
}
onCommit: {
editTextChaged(textbox.text)
@@ -344,7 +348,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
})
}
}
@@ -442,15 +448,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 +465,6 @@ FluContentPage{
table_view.dataSource = data
}
}
-
FluButton{
text: qsTr("Add a row of Data")
onClicked: {
@@ -469,18 +474,15 @@ FluContentPage{
FluButton{
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!"))
}
-
}
}
-
}
}
@@ -500,20 +502,19 @@ FluContentPage{
{
title: table_view.customItem(com_column_checbox,{checked:true}),
dataIndex: 'checkbox',
- width:100,
- minimumWidth:100,
- maximumWidth:100
- },
- {
- title: table_view.customItem(com_column_update_title,{title:qsTr("Avatar")}),
- dataIndex: 'avatar',
- width:100
+ 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',
@@ -549,8 +550,7 @@ FluContentPage{
title: qsTr("Options"),
dataIndex: 'action',
width:160,
- minimumWidth:160,
- maximumWidth:160
+ frozen:true
}
]
}
@@ -620,7 +620,6 @@ FluContentPage{
for(var i=0;i {
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/example/qml/window/MainWindow.qml b/example/qml/window/MainWindow.qml
index 8b92bedc..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: 680
- 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 70040b5e..591a0eb9 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)
@@ -62,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)
@@ -179,6 +197,34 @@ 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)
+ 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/FluApp.cpp b/src/FluApp.cpp
index 36bfd3fa..da3ab8d6 100644
--- a/src/FluApp.cpp
+++ b/src/FluApp.cpp
@@ -1,6 +1,5 @@
#include "FluApp.h"
-#include
#include
#include
#include
@@ -17,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();
@@ -32,9 +32,9 @@ 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"));
+ 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/FluApp.h b/src/FluApp.h
index 89934208..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,9 +35,9 @@ 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 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 6f92e2c7..90ccf947 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;
@@ -35,15 +35,28 @@ 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);
+ 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) {
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;
@@ -73,7 +86,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);
}
@@ -92,25 +105,40 @@ void FluFrameless::componentComplete() {
HWND hwnd = reinterpret_cast(window()->winId());
DWORD style = ::GetWindowLongPtr(hwnd, GWL_STYLE);
if (_fixSize) {
- ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_CAPTION);
+#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 {
- ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_CAPTION);
+#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] {
::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()) {
+ 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] {
@@ -144,24 +172,39 @@ 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]);
- bool isMaximum = ::IsZoomed(hwnd);
- if (!isMaximum){
- if (clientRect->top != 0)
- {
- clientRect->top -= 1;
- clientRect->bottom -= 1;
- }
- } 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;
+ } 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;
+ 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 (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) {
+ 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;
+ }
+#endif
_setMaximizeHovered(false);
*result = WVR_REDRAW;
return true;
@@ -219,17 +262,32 @@ void FluFrameless::componentComplete() {
*result = HTCLIENT;
return true;
} else if (uMsg == WM_NCPAINT) {
- if(isCompositionEnabled()){
+#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(isCompositionEnabled()){
- return false;
- }
*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();
+ 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);
+#endif
+ 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);
@@ -244,11 +302,11 @@ 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());
+ 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));
@@ -286,9 +344,16 @@ 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);
auto hMenu = ::GetSystemMenu(hwnd, FALSE);
if (_isMaximized() || _isFullScreen()) {
::EnableMenuItem(hMenu, SC_MOVE, MFS_DISABLED);
@@ -304,12 +369,11 @@ 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);
- if (result != FALSE) {
+ const int result = ::TrackPopupMenu(hMenu, (TPM_RETURNCMD | (QGuiApplication::isRightToLeft() ? TPM_RIGHTALIGN : TPM_LEFTALIGN)), nativePos.x(),
+ nativePos.y(), 0, hwnd, nullptr);
+ if (result) {
::PostMessageW(hwnd, WM_SYSCOMMAND, result, 0);
}
- ::SetWindowLongPtr(hwnd, GWL_STYLE, style & ~WS_SYSMENU);
#endif
}
@@ -334,36 +398,40 @@ 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) {
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/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/FluTableModel.cpp b/src/FluTableModel.cpp
new file mode 100644
index 00000000..eb22de64
--- /dev/null
+++ b/src/FluTableModel.cpp
@@ -0,0 +1,63 @@
+#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"}
+ };
+}
+
+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..a734aa80
--- /dev/null
+++ b/src/FluTableModel.h
@@ -0,0 +1,46 @@
+#ifndef FLUTABLEMODEL_H
+#define FLUTABLEMODEL_H
+
+#include
+#include
+#include
+#include "stdafx.h"
+
+class FluTableModel : public QAbstractTableModel {
+Q_OBJECT
+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;
+
+ 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/FluTableSortProxyModel.cpp b/src/FluTableSortProxyModel.cpp
index 644f652a..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,15 +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.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 7a959f41..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);
@@ -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/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 020e8af8..f080a034 100644
--- a/src/FluTreeModel.cpp
+++ b/src/FluTreeModel.cpp
@@ -1,25 +1,14 @@
#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;
}
-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, _rows.at(row));
-}
-
int FluTreeModel::rowCount(const QModelIndex &parent) const {
return _rows.count();
}
@@ -265,12 +254,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 d8eda4f5..e9d5f48b 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);
@@ -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,
@@ -108,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/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 a1704d39..28a97aca 100644
--- a/src/FluentUI.cpp
+++ b/src/FluentUI.cpp
@@ -15,6 +15,8 @@
#include "FluQrCodeItem.h"
#include "FluTableSortProxyModel.h"
#include "FluFrameless.h"
+#include "FluTableModel.h"
+#include "FluHotkey.h"
void FluentUI::registerTypes(QQmlEngine *engine) {
initializeEngine(engine, _uri);
@@ -32,8 +34,10 @@ 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, "FluHotkey");
qmlRegisterType(uri, major, minor, "FluTableSortProxyModel");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluAcrylic.qml"), uri, major, minor, "FluAcrylic");
@@ -132,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");
@@ -146,14 +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/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/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/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/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/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/Qt5/imports/FluentUI/Controls/FluRadioButtons.qml b/src/Qt5/imports/FluentUI/Controls/FluRadioButtons.qml
index b725950a..24c55a65 100644
--- a/src/Qt5/imports/FluentUI/Controls/FluRadioButtons.qml
+++ b/src/Qt5/imports/FluentUI/Controls/FluRadioButtons.qml
@@ -3,44 +3,87 @@ import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import FluentUI 1.0
-ColumnLayout {
- default property alias buttons: control.data
+Item{
+ id:control
+ default property list buttons
property int currentIndex : -1
+ property int spacing: 8
+ property int orientation: Qt.Vertical
property bool disabled: false
property bool manuallyDisabled: false
- id:control
- onCurrentIndexChanged: {
- for(var i = 0;i=0 && currentIndex{
- 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 model: item_table_mouse._model
+ 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){
@@ -304,15 +367,53 @@ 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.isRowSelected
+ visible: item_table_mouse.isRowSelected
Rectangle{
width: 1
height: parent.height
@@ -343,6 +444,11 @@ Rectangle {
}
}
}
+
+ onWidthChanged:{
+ table_view.forceLayout()
+ }
+
MouseArea{
id:layout_mouse_table
hoverEnabled: true
@@ -364,30 +470,8 @@ Rectangle {
anchors.fill: parent
ScrollBar.horizontal:scroll_bar_h
ScrollBar.vertical:scroll_bar_v
- columnWidthProvider: function(column) {
- var columnObject = d.columns_data[column]
- var width = columnObject.width
- if(width){
- return width
- }
- var minimumWidth = columnObject.minimumWidth
- if(minimumWidth){
- return minimumWidth
- }
- return d.defaultItemWidth
- }
- rowHeightProvider: function(row) {
- var rowObject = control.getRow(row)
- var height = rowObject.height
- if(height){
- return height
- }
- var minimumHeight = rowObject._minimumHeight
- if(minimumHeight){
- return minimumHeight
- }
- return d.defaultItemHeight
- }
+ columnWidthProvider: control.columnWidthProvider
+ rowHeightProvider: control.rowHeightProvider
model: table_sort_model
clip: true
onRowsChanged: {
@@ -395,70 +479,51 @@ 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[d.columns_data[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
+ 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 int columnIndex: column
- readonly property var columnObject : d.columns_data[column]
- implicitWidth: {
- return (item_column_loader.item && item_column_loader.item.implicitWidth) + (cellPadding * 2)
+ property var _model: model
+ 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
+ readonly property bool isHide: {
+ if(isHeaderHorizontal){
+ return false
+ }
+ 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)
+ }
+ 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 Math.max(TableView.view.height,Number.MIN_VALUE)
}
- 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
@@ -479,7 +544,7 @@ Rectangle {
width: 1
height: parent.height
anchors.left: parent.left
- visible: column !== 0
+ visible: column_item_control._index !== 0
color:"#00000000"
}
Rectangle{
@@ -488,7 +553,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
@@ -510,22 +575,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.title
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: column_item_control._index
width: parent.width
height: parent.height
sourceComponent: {
- if(typeof(modelData) == "object"){
- return modelData.comId
+ if(isObject){
+ return display.comId
}
return com_column_text
}
@@ -537,7 +603,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 :
@@ -557,9 +623,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
}
@@ -569,7 +635,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()
}
@@ -582,7 +648,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)
@@ -648,9 +714,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)=>{
@@ -668,11 +734,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
}
@@ -682,8 +748,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()
}
}
@@ -693,7 +759,7 @@ Rectangle {
id:com_column_text
FluText {
id: column_text
- text: modelData
+ text: String(display)
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
@@ -796,6 +862,138 @@ 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)
+ }
+ visible: table_view.rows !== 0
+ 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{
+ 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
+ interactive: false
+ clip: true
+ contentWidth: width
+ anchors{
+ left: parent.left
+ right: parent.right
+ top: parent.top
+ bottom: item_table_frozen.top
+ }
+ delegate: com_column_header_delegate
+ Component.onCompleted: {
+ item_table_frozen_header.forceLayout()
+ }
+ }
+ Connections{
+ target: table_view
+ function onWidthChanged() {
+ item_table_frozen_header.forceLayout()
+ }
+
+ }
+ }
+ }
+ Repeater{
+ Component.onCompleted: {
+ model = control.columnSource
+ }
+ delegate: FluLoader{
+ 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){
+ 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-width
+ for(var i=0;i<_index;i++){
+ var item = control.columnSource[i]
+ if(item.frozen){
+ minX = minX + table_view.columnWidthProvider(i)
+ }
+ }
+ for(i=_index+1;i=0 && rowIndex" }
+ Property { name: "rows"; type: "QList" }
+ Property { name: "rowCount"; type: "int"; isReadonly: true }
+ Method { name: "clear" }
Method {
name: "getRow"
type: "QVariant"
@@ -278,6 +296,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 +356,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 +372,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 +442,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 +563,7 @@ Module {
}
Component {
name: "FluTreeModel"
- prototype: "QAbstractItemModel"
+ prototype: "QAbstractTableModel"
exports: ["FluentUI/FluTreeModel 1.0"]
exportMetaObjectRevisions: [0]
Property { name: "dataSourceSize"; type: "int" }
@@ -2316,6 +2380,7 @@ Module {
Parameter { name: "selection"; type: "QItemSelection" }
}
}
+ Component { name: "QAbstractTableModel"; prototype: "QAbstractItemModel" }
Component {
name: "QSortFilterProxyModel"
prototype: "QAbstractProxyModel"
@@ -2442,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_14"
+ type: "FluLoader_QMLTYPE_16"
isReadonly: true
isPointer: true
}
@@ -2493,6 +2558,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 +2761,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" }
@@ -3043,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 {
@@ -3162,15 +3230,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_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_33"; isPointer: true }
- Property { name: "navItemExpanderRightMenu"; type: "FluMenu_QMLTYPE_33"; 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" }
@@ -3178,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
}
@@ -3246,6 +3314,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 +3540,18 @@ 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" }
+ Property { name: "disabled"; type: "bool" }
+ Property { name: "manuallyDisabled"; type: "bool" }
}
Component {
prototype: "QQuickRangeSlider"
@@ -3632,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" }
@@ -3757,6 +3834,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" }
@@ -3764,10 +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: "sourceModel"; type: "QQmlTableModel"; isReadonly: true; isPointer: true }
+ Property { name: "view"; type: "QQuickTableView"; isReadonly: true; isPointer: true }
Method { name: "closeEditor"; type: "QVariant" }
Method { name: "resetPosition"; type: "QVariant" }
Method {
@@ -3803,6 +3883,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"
@@ -4013,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 {
@@ -4068,6 +4156,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"
@@ -4103,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 {
@@ -4116,6 +4206,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 +4228,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 +4259,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"
@@ -4197,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 {
@@ -4210,6 +4309,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"
diff --git a/src/Qt6/imports/FluentUI/Controls/FluColorPicker.qml b/src/Qt6/imports/FluentUI/Controls/FluColorPicker.qml
index c22eb090..59798006 100644
--- a/src/Qt6/imports/FluentUI/Controls/FluColorPicker.qml
+++ b/src/Qt6/imports/FluentUI/Controls/FluColorPicker.qml
@@ -1,5 +1,6 @@
import QtQuick
import QtQuick.Controls
+import QtQuick.Controls.Basic
import QtQuick.Layouts
import QtQuick.Window
import FluentUI
diff --git a/src/Qt6/imports/FluentUI/Controls/FluComboBox.qml b/src/Qt6/imports/FluentUI/Controls/FluComboBox.qml
index 6a89782b..70e01e81 100644
--- a/src/Qt6/imports/FluentUI/Controls/FluComboBox.qml
+++ b/src/Qt6/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/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/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/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
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)
}
-
}
diff --git a/src/Qt6/imports/FluentUI/Controls/FluRadioButtons.qml b/src/Qt6/imports/FluentUI/Controls/FluRadioButtons.qml
index a22b0795..7bb82e69 100644
--- a/src/Qt6/imports/FluentUI/Controls/FluRadioButtons.qml
+++ b/src/Qt6/imports/FluentUI/Controls/FluRadioButtons.qml
@@ -4,44 +4,87 @@ import QtQuick.Controls.Basic
import QtQuick.Layouts
import FluentUI
-ColumnLayout {
- default property alias buttons: control.data
+Item{
+ id:control
+ default property list buttons
property int currentIndex : -1
+ property int spacing: 8
+ property int orientation: Qt.Vertical
property bool disabled: false
property bool manuallyDisabled: false
- id:control
- onCurrentIndexChanged: {
- for(var i = 0;i=0 && currentIndex{
- 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 model: item_table_mouse._model
+ 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){
@@ -304,15 +367,53 @@ 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.isRowSelected
+ visible: item_table_mouse.isRowSelected
Rectangle{
width: 1
height: parent.height
@@ -343,6 +444,11 @@ Rectangle {
}
}
}
+
+ onWidthChanged:{
+ table_view.forceLayout()
+ }
+
MouseArea{
id:layout_mouse_table
hoverEnabled: true
@@ -364,30 +470,8 @@ Rectangle {
anchors.fill: parent
ScrollBar.horizontal:scroll_bar_h
ScrollBar.vertical:scroll_bar_v
- columnWidthProvider: function(column) {
- var columnObject = d.columns_data[column]
- var width = columnObject.width
- if(width){
- return width
- }
- var minimumWidth = columnObject.minimumWidth
- if(minimumWidth){
- return minimumWidth
- }
- return d.defaultItemWidth
- }
- rowHeightProvider: function(row) {
- var rowObject = control.getRow(row)
- var height = rowObject.height
- if(height){
- return height
- }
- var minimumHeight = rowObject._minimumHeight
- if(minimumHeight){
- return minimumHeight
- }
- return d.defaultItemHeight
- }
+ columnWidthProvider: control.columnWidthProvider
+ rowHeightProvider: control.rowHeightProvider
model: table_sort_model
clip: true
onRowsChanged: {
@@ -395,70 +479,51 @@ 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[d.columns_data[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
+ 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 int columnIndex: column
- readonly property var columnObject : d.columns_data[column]
- implicitWidth: {
- return (item_column_loader.item && item_column_loader.item.implicitWidth) + (cellPadding * 2)
+ property var _model: model
+ 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
+ readonly property bool isHide: {
+ if(isHeaderHorizontal){
+ return false
+ }
+ 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)
+ }
+ 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 Math.max(TableView.view.height,Number.MIN_VALUE)
}
- 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
@@ -479,7 +544,7 @@ Rectangle {
width: 1
height: parent.height
anchors.left: parent.left
- visible: column !== 0
+ visible: column_item_control._index !== 0
color:"#00000000"
}
Rectangle{
@@ -488,7 +553,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
@@ -510,22 +575,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.title
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: column_item_control._index
width: parent.width
height: parent.height
sourceComponent: {
- if(typeof(modelData) == "object"){
- return modelData.comId
+ if(isObject){
+ return display.comId
}
return com_column_text
}
@@ -537,7 +603,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 :
@@ -557,9 +623,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
}
@@ -569,7 +635,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()
}
@@ -582,7 +648,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)
@@ -648,9 +714,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)=>{
@@ -668,11 +734,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
}
@@ -682,8 +748,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()
}
}
@@ -693,7 +759,7 @@ Rectangle {
id:com_column_text
FluText {
id: column_text
- text: modelData
+ text: String(display)
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
@@ -796,6 +862,138 @@ 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)
+ }
+ visible: table_view.rows !== 0
+ 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{
+ 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
+ interactive: false
+ clip: true
+ contentWidth: width
+ anchors{
+ left: parent.left
+ right: parent.right
+ top: parent.top
+ bottom: item_table_frozen.top
+ }
+ delegate: com_column_header_delegate
+ Component.onCompleted: {
+ item_table_frozen_header.forceLayout()
+ }
+ }
+ Connections{
+ target: table_view
+ function onWidthChanged() {
+ item_table_frozen_header.forceLayout()
+ }
+
+ }
+ }
+ }
+ Repeater{
+ Component.onCompleted: {
+ model = control.columnSource
+ }
+ delegate: FluLoader{
+ 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){
+ 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-width
+ for(var i=0;i<_index;i++){
+ var item = control.columnSource[i]
+ if(item.frozen){
+ minX = minX + table_view.columnWidthProvider(i)
+ }
+ }
+ for(i=_index+1;i=0 && rowIndexFluColorPicker
-
+
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;
+ }
+}