Compare commits

..

37 Commits

Author SHA1 Message Date
朱子楚\zhuzi
ef65183320 Merge branch '1.7.6-dev' 2024-06-04 17:53:17 +08:00
朱子楚\zhuzi
5cfd9824a8 update 2024-05-19 13:40:36 +08:00
朱子楚\zhuzi
a27554bfb2 update 2024-05-17 21:27:41 +08:00
朱子楚\zhuzi
7a67ccc1e1 update 2024-05-17 21:19:10 +08:00
朱子楚\zhuzi
39f58278ff update 2024-05-16 01:26:59 +08:00
朱子楚\zhuzi
354f7f2e3e add hotkey 2024-05-16 00:35:04 +08:00
朱子楚\zhuzi
876b230141 update 2024-05-13 18:28:28 +08:00
朱子楚\zhuzi
2367c6978a update 2024-05-11 22:21:11 +08:00
朱子楚\zhuzi
6b941697b0 update 2024-05-11 21:45:45 +08:00
朱子楚\zhuzi
4997b991db update 2024-05-10 19:23:30 +08:00
朱子楚\zhuzi
cd3abc01e9 fix bug 2024-05-10 19:20:01 +08:00
朱子楚\zhuzi
e6c4b79298 Merge branch 'main' into 1.7.6-dev
# Conflicts:
#	src/Qt5/imports/FluentUI/Controls/FluRadioButtons.qml
#	src/Qt6/imports/FluentUI/Controls/FluRadioButtons.qml
2024-05-10 12:26:14 +08:00
朱子楚\zhuzi
402579f32a update 2024-05-08 18:58:22 +08:00
朱子楚\zhuzi
655eff4f62 update 2024-05-07 22:16:42 +08:00
朱子楚\zhuzi
b916221d9f update 2024-05-07 21:47:40 +08:00
朱子楚\zhuzi
4cca680029 update 2024-05-05 22:05:18 +08:00
朱子楚\zhuzi
fe2543ab4d fix bug 2024-05-05 22:03:41 +08:00
朱子楚\zhuzi
394a42cb94 update 2024-05-05 21:33:47 +08:00
朱子楚\zhuzi
ac253a3de5 fix bug 2024-05-05 20:32:48 +08:00
朱子楚\zhuzi
0d61e33ef1 update 2024-05-05 15:52:46 +08:00
朱子楚\zhuzi
132ab12c32 update 2024-05-05 00:06:01 +08:00
朱子楚\zhuzi
af270951da update 2024-05-04 23:56:20 +08:00
朱子楚\zhuzi
18685b17ec update 2024-05-03 16:56:40 +08:00
朱子楚\zhuzi
0eb4d9f346 update 2024-05-03 01:54:38 +08:00
朱子楚\zhuzi
8015dcc2f1 update 2024-05-02 23:52:30 +08:00
朱子楚\zhuzi
0b1755e9eb update 2024-05-01 01:11:25 +08:00
朱子楚\zhuzi
e471d5a230 update 2024-04-30 19:38:05 +08:00
朱子楚\zhuzi
f922978338 update 2024-04-30 19:27:57 +08:00
朱子楚\zhuzi
91bda2a22c update 2024-04-30 13:07:46 +08:00
朱子楚\zhuzi
6638fe2e06 update 2024-04-30 13:06:27 +08:00
朱子楚\zhuzi
bf001d99d2 update 2024-04-30 12:59:09 +08:00
朱子楚\zhuzi
30531079b5 update 2024-04-29 10:12:34 +08:00
朱子楚\zhuzi
a746bc2684 update 2024-04-28 21:05:43 +08:00
朱子楚\zhuzi
96a6d0e7fa update 2024-04-28 20:22:05 +08:00
朱子楚\zhuzi
c733f3c60e update 2024-04-28 18:38:37 +08:00
朱子楚\zhuzi
effd9f3058 update 2024-04-28 18:19:21 +08:00
朱子楚\zhuzi
d93aac3518 update 2024-04-28 15:56:37 +08:00
65 changed files with 4031 additions and 1066 deletions

View File

@ -34,6 +34,38 @@ providing powerful tools and support for this project.
For more information about the Qt project, For more information about the Qt project,
please visit the official Qt website (https://www.qt.io/). 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 framelesshelper

View File

@ -209,6 +209,7 @@
<file>qml/window/FluentInitializrWindow.qml</file> <file>qml/window/FluentInitializrWindow.qml</file>
<file>qml/page/T_OpenGL.qml</file> <file>qml/page/T_OpenGL.qml</file>
<file>qml/page/T_Icons.qml</file> <file>qml/page/T_Icons.qml</file>
<file>qml/window/HotkeyWindow.qml</file>
</qresource> </qresource>
<qresource prefix="/"/> <qresource prefix="/"/>
</RCC> </RCC>

View File

@ -1,6 +1,54 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS> <!DOCTYPE TS>
<TS version="2.1" language="en_US"> <TS version="2.1" language="en_US">
<context>
<name>App</name>
<message>
<location filename="qml/App.qml" line="61"/>
<source>Quit</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/App.qml" line="68"/>
<source>Test1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/App.qml" line="75"/>
<source>Test2</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/App.qml" line="82"/>
<source>Test3</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/App.qml" line="89"/>
<source>Test4</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/App.qml" line="96"/>
<source>Test5</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/App.qml" line="103"/>
<source>Test6</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/App.qml" line="110"/>
<source>Test7</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/App.qml" line="117"/>
<source>Test8</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>CodeExpander</name> <name>CodeExpander</name>
<message> <message>
@ -71,6 +119,14 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>HotkeyWindow</name>
<message>
<location filename="qml/window/HotkeyWindow.qml" line="11"/>
<source>Hotkey</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>HotloadWindow</name> <name>HotloadWindow</name>
<message> <message>
@ -1273,25 +1329,31 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
</message> </message>
<message> <message>
<location filename="qml/page/T_GroupBox.qml" line="17"/> <location filename="qml/page/T_GroupBox.qml" line="17"/>
<location filename="qml/page/T_GroupBox.qml" line="27"/> <location filename="qml/page/T_GroupBox.qml" line="35"/>
<source>E-mail</source> <source>E-mail</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_GroupBox.qml" line="18"/> <location filename="qml/page/T_GroupBox.qml" line="18"/>
<location filename="qml/page/T_GroupBox.qml" line="28"/> <location filename="qml/page/T_GroupBox.qml" line="36"/>
<source>Calendar</source> <source>Calendar</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_GroupBox.qml" line="19"/> <location filename="qml/page/T_GroupBox.qml" line="19"/>
<location filename="qml/page/T_GroupBox.qml" line="29"/> <location filename="qml/page/T_GroupBox.qml" line="37"/>
<source>Contacts</source> <source>Contacts</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_GroupBox.qml" line="24"/> <location filename="qml/page/T_GroupBox.qml" line="24"/>
<source>RadioButton Group</source> <source>RadioButton Group</source>
<oldsource>RadioButton Group111111111111111111111111</oldsource>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/page/T_GroupBox.qml" line="46"/>
<source>Disabled</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
@ -1331,12 +1393,7 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_Icons.qml" line="20"/> <location filename="qml/page/T_Icons.qml" line="51"/>
<source>Search</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/page/T_Icons.qml" line="60"/>
<source>You Copied </source> <source>You Copied </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -1362,51 +1419,82 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_InfoBar.qml" line="23"/> <location filename="qml/page/T_InfoBar.qml" line="27"/>
<source>Info</source> <source>Info</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_InfoBar.qml" line="25"/> <location filename="qml/page/T_InfoBar.qml" line="29"/>
<location filename="qml/page/T_InfoBar.qml" line="49"/> <location filename="qml/page/T_InfoBar.qml" line="53"/>
<source>This is an InfoBar in the Info Style</source> <source>This is an InfoBar in the Info Style</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_InfoBar.qml" line="29"/> <location filename="qml/page/T_InfoBar.qml" line="33"/>
<source>Warning</source> <source>Warning</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_InfoBar.qml" line="31"/> <location filename="qml/page/T_InfoBar.qml" line="35"/>
<source>This is an InfoBar in the Warning Style</source> <source>This is an InfoBar in the Warning Style</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_InfoBar.qml" line="37"/> <location filename="qml/page/T_InfoBar.qml" line="41"/>
<source>This is an InfoBar in the Error Style</source> <source>This is an InfoBar in the Error Style</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_InfoBar.qml" line="43"/> <location filename="qml/page/T_InfoBar.qml" line="47"/>
<source>This is an InfoBar in the Success Style</source> <source>This is an InfoBar in the Success Style</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_InfoBar.qml" line="47"/> <location filename="qml/page/T_InfoBar.qml" line="51"/>
<source>InfoBar that needs to be turned off manually</source> <source>InfoBar that needs to be turned off manually</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_InfoBar.qml" line="49"/> <location filename="qml/page/T_InfoBar.qml" line="53"/>
<source>Manual shutdown is supported</source> <source>Manual shutdown is supported</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="qml/page/T_InfoBar.qml" line="59"/>
<source>Manually close the info message box</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/page/T_InfoBar.qml" line="64"/>
<location filename="qml/page/T_InfoBar.qml" line="74"/>
<location filename="qml/page/T_InfoBar.qml" line="84"/>
<source>close &apos;%1&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/page/T_InfoBar.qml" line="64"/>
<location filename="qml/page/T_InfoBar.qml" line="74"/>
<location filename="qml/page/T_InfoBar.qml" line="84"/>
<source>show &apos;%1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/page/T_InfoBar.qml" line="70"/>
<location filename="qml/page/T_InfoBar.qml" line="80"/>
<location filename="qml/page/T_InfoBar.qml" line="90"/>
<source>This is an &apos;%1&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/page/T_InfoBar.qml" line="94"/>
<source>clear all info</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>T_LineChart</name> <name>T_LineChart</name>
<message> <message>
<location filename="qml/chart/T_LineChart.qml" line="10"/> <location filename="qml/chart/T_LineChart.qml" line="11"/>
<source>Line Chart</source> <source>Line Chart</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -1704,22 +1792,30 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
</message> </message>
<message> <message>
<location filename="qml/page/T_RadioButton.qml" line="38"/> <location filename="qml/page/T_RadioButton.qml" line="38"/>
<location filename="qml/page/T_RadioButton.qml" line="80"/> <location filename="qml/page/T_RadioButton.qml" line="79"/>
<location filename="qml/page/T_RadioButton.qml" line="130"/>
<location filename="qml/page/T_RadioButton.qml" line="181"/>
<source>Disabled</source> <source>Disabled</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_RadioButton.qml" line="63"/> <location filename="qml/page/T_RadioButton.qml" line="64"/>
<location filename="qml/page/T_RadioButton.qml" line="115"/>
<location filename="qml/page/T_RadioButton.qml" line="166"/>
<source>Radio Button_1</source> <source>Radio Button_1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_RadioButton.qml" line="67"/> <location filename="qml/page/T_RadioButton.qml" line="67"/>
<location filename="qml/page/T_RadioButton.qml" line="118"/>
<location filename="qml/page/T_RadioButton.qml" line="169"/>
<source>Radio Button_2</source> <source>Radio Button_2</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_RadioButton.qml" line="71"/> <location filename="qml/page/T_RadioButton.qml" line="70"/>
<location filename="qml/page/T_RadioButton.qml" line="121"/>
<location filename="qml/page/T_RadioButton.qml" line="172"/>
<source>Radio Button_3</source> <source>Radio Button_3</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -1947,88 +2043,98 @@ Some contents...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="73"/> <location filename="qml/page/T_TableView.qml" line="72"/>
<source>Modify the column name</source> <source>Modify the column name</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="74"/> <location filename="qml/page/T_TableView.qml" line="73"/>
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="92"/> <location filename="qml/page/T_TableView.qml" line="91"/>
<source>OK</source> <source>OK</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="134"/> <location filename="qml/page/T_TableView.qml" line="133"/>
<source>Search</source> <source>Search</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="177"/> <location filename="qml/page/T_TableView.qml" line="176"/>
<location filename="qml/page/T_TableView.qml" line="513"/> <location filename="qml/page/T_TableView.qml" line="508"/>
<source>Name</source> <source>Name</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="211"/> <location filename="qml/page/T_TableView.qml" line="210"/>
<source>Delete</source> <source>Delete</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="218"/> <location filename="qml/page/T_TableView.qml" line="217"/>
<source>Edit</source> <source>Edit</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="237"/> <location filename="qml/page/T_TableView.qml" line="236"/>
<source>Select All</source> <source>Select All</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="358"/> <location filename="qml/page/T_TableView.qml" line="364"/>
<source>Age</source> <source>Age</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="433"/> <location filename="qml/page/T_TableView.qml" line="439"/>
<source>Clear All</source> <source>Clear All</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="470"/> <location filename="qml/page/T_TableView.qml" line="446"/>
<source>Delete Selection</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/page/T_TableView.qml" line="469"/>
<source>Add a row of Data</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/page/T_TableView.qml" line="475"/>
<source>Insert a Row</source> <source>Insert a Row</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="478"/> <location filename="qml/page/T_TableView.qml" line="482"/>
<source>Focus not acquired: Please click any item in the form as the target for insertion!</source> <source>Focus not acquired: Please click any item in the form as the target for insertion!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="508"/> <location filename="qml/page/T_TableView.qml" line="513"/>
<source>Avatar</source> <source>Avatar</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="526"/> <location filename="qml/page/T_TableView.qml" line="527"/>
<source>Address</source> <source>Address</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="534"/> <location filename="qml/page/T_TableView.qml" line="535"/>
<source>Nickname</source> <source>Nickname</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="541"/> <location filename="qml/page/T_TableView.qml" line="542"/>
<source>Long String</source> <source>Long String</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="549"/> <location filename="qml/page/T_TableView.qml" line="550"/>
<source>Options</source> <source>Options</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -2042,16 +2148,6 @@ Some contents...</source>
<source>Next&gt;</source> <source>Next&gt;</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="qml/page/T_TableView.qml" line="440"/>
<source>Delete Selection</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/page/T_TableView.qml" line="464"/>
<source>Add a row of Data</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>T_Text</name> <name>T_Text</name>
@ -2203,12 +2299,12 @@ Some contents...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_Timeline.qml" line="115"/> <location filename="qml/page/T_Timeline.qml" line="114"/>
<source>Append</source> <source>Append</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_Timeline.qml" line="121"/> <location filename="qml/page/T_Timeline.qml" line="120"/>
<source>clear</source> <source>clear</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>

View File

@ -1,6 +1,54 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS> <!DOCTYPE TS>
<TS version="2.1" language="zh_CN"> <TS version="2.1" language="zh_CN">
<context>
<name>App</name>
<message>
<location filename="qml/App.qml" line="61"/>
<source>Quit</source>
<translation type="unfinished">退</translation>
</message>
<message>
<location filename="qml/App.qml" line="68"/>
<source>Test1</source>
<translation type="unfinished">1</translation>
</message>
<message>
<location filename="qml/App.qml" line="75"/>
<source>Test2</source>
<translation type="unfinished">2</translation>
</message>
<message>
<location filename="qml/App.qml" line="82"/>
<source>Test3</source>
<translation type="unfinished">3</translation>
</message>
<message>
<location filename="qml/App.qml" line="89"/>
<source>Test4</source>
<translation type="unfinished">4</translation>
</message>
<message>
<location filename="qml/App.qml" line="96"/>
<source>Test5</source>
<translation type="unfinished">5</translation>
</message>
<message>
<location filename="qml/App.qml" line="103"/>
<source>Test6</source>
<translation type="unfinished">6</translation>
</message>
<message>
<location filename="qml/App.qml" line="110"/>
<source>Test7</source>
<translation type="unfinished">7</translation>
</message>
<message>
<location filename="qml/App.qml" line="117"/>
<source>Test8</source>
<translation type="unfinished">8</translation>
</message>
</context>
<context> <context>
<name>CodeExpander</name> <name>CodeExpander</name>
<message> <message>
@ -71,6 +119,14 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>HotkeyWindow</name>
<message>
<location filename="qml/window/HotkeyWindow.qml" line="11"/>
<source>Hotkey</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>HotloadWindow</name> <name>HotloadWindow</name>
<message> <message>
@ -1359,27 +1415,33 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
</message> </message>
<message> <message>
<location filename="qml/page/T_GroupBox.qml" line="17"/> <location filename="qml/page/T_GroupBox.qml" line="17"/>
<location filename="qml/page/T_GroupBox.qml" line="27"/> <location filename="qml/page/T_GroupBox.qml" line="35"/>
<source>E-mail</source> <source>E-mail</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_GroupBox.qml" line="18"/> <location filename="qml/page/T_GroupBox.qml" line="18"/>
<location filename="qml/page/T_GroupBox.qml" line="28"/> <location filename="qml/page/T_GroupBox.qml" line="36"/>
<source>Calendar</source> <source>Calendar</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_GroupBox.qml" line="19"/> <location filename="qml/page/T_GroupBox.qml" line="19"/>
<location filename="qml/page/T_GroupBox.qml" line="29"/> <location filename="qml/page/T_GroupBox.qml" line="37"/>
<source>Contacts</source> <source>Contacts</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_GroupBox.qml" line="24"/> <location filename="qml/page/T_GroupBox.qml" line="24"/>
<source>RadioButton Group</source> <source>RadioButton Group</source>
<oldsource>RadioButton Group111111111111111111111111</oldsource>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="qml/page/T_GroupBox.qml" line="46"/>
<source>Disabled</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>T_Home</name> <name>T_Home</name>
@ -1417,12 +1479,11 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_Icons.qml" line="20"/>
<source>Search</source> <source>Search</source>
<translation type="unfinished"></translation> <translation type="obsolete"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_Icons.qml" line="60"/> <location filename="qml/page/T_Icons.qml" line="51"/>
<source>You Copied </source> <source>You Copied </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -1452,46 +1513,77 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_InfoBar.qml" line="23"/> <location filename="qml/page/T_InfoBar.qml" line="27"/>
<source>Info</source> <source>Info</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_InfoBar.qml" line="25"/> <location filename="qml/page/T_InfoBar.qml" line="29"/>
<location filename="qml/page/T_InfoBar.qml" line="49"/> <location filename="qml/page/T_InfoBar.qml" line="53"/>
<source>This is an InfoBar in the Info Style</source> <source>This is an InfoBar in the Info Style</source>
<translation type="unfinished">Info样式的信息栏</translation> <translation type="unfinished">Info样式的信息栏</translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_InfoBar.qml" line="29"/> <location filename="qml/page/T_InfoBar.qml" line="33"/>
<source>Warning</source> <source>Warning</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_InfoBar.qml" line="31"/> <location filename="qml/page/T_InfoBar.qml" line="35"/>
<source>This is an InfoBar in the Warning Style</source> <source>This is an InfoBar in the Warning Style</source>
<translation type="unfinished">Warning样式的信息栏</translation> <translation type="unfinished">Warning样式的信息栏</translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_InfoBar.qml" line="37"/> <location filename="qml/page/T_InfoBar.qml" line="41"/>
<source>This is an InfoBar in the Error Style</source> <source>This is an InfoBar in the Error Style</source>
<translation type="unfinished">Error样式的信息栏</translation> <translation type="unfinished">Error样式的信息栏</translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_InfoBar.qml" line="43"/> <location filename="qml/page/T_InfoBar.qml" line="47"/>
<source>This is an InfoBar in the Success Style</source> <source>This is an InfoBar in the Success Style</source>
<translation type="unfinished">Success样式的信息栏</translation> <translation type="unfinished">Success样式的信息栏</translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_InfoBar.qml" line="47"/> <location filename="qml/page/T_InfoBar.qml" line="51"/>
<source>InfoBar that needs to be turned off manually</source> <source>InfoBar that needs to be turned off manually</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_InfoBar.qml" line="49"/> <location filename="qml/page/T_InfoBar.qml" line="53"/>
<source>Manual shutdown is supported</source> <source>Manual shutdown is supported</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="qml/page/T_InfoBar.qml" line="59"/>
<source>Manually close the info message box</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/page/T_InfoBar.qml" line="64"/>
<location filename="qml/page/T_InfoBar.qml" line="74"/>
<location filename="qml/page/T_InfoBar.qml" line="84"/>
<source>close &apos;%1&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/page/T_InfoBar.qml" line="64"/>
<location filename="qml/page/T_InfoBar.qml" line="74"/>
<location filename="qml/page/T_InfoBar.qml" line="84"/>
<source>show &apos;%1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/page/T_InfoBar.qml" line="70"/>
<location filename="qml/page/T_InfoBar.qml" line="80"/>
<location filename="qml/page/T_InfoBar.qml" line="90"/>
<source>This is an &apos;%1&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/page/T_InfoBar.qml" line="94"/>
<source>clear all info</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>Loading...</source> <source>Loading...</source>
<translation type="obsolete">...</translation> <translation type="obsolete">...</translation>
@ -1500,7 +1592,7 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
<context> <context>
<name>T_LineChart</name> <name>T_LineChart</name>
<message> <message>
<location filename="qml/chart/T_LineChart.qml" line="10"/> <location filename="qml/chart/T_LineChart.qml" line="11"/>
<source>Line Chart</source> <source>Line Chart</source>
<translation type="unfinished">线</translation> <translation type="unfinished">线</translation>
</message> </message>
@ -1808,22 +1900,30 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
</message> </message>
<message> <message>
<location filename="qml/page/T_RadioButton.qml" line="38"/> <location filename="qml/page/T_RadioButton.qml" line="38"/>
<location filename="qml/page/T_RadioButton.qml" line="80"/> <location filename="qml/page/T_RadioButton.qml" line="79"/>
<location filename="qml/page/T_RadioButton.qml" line="130"/>
<location filename="qml/page/T_RadioButton.qml" line="181"/>
<source>Disabled</source> <source>Disabled</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_RadioButton.qml" line="63"/> <location filename="qml/page/T_RadioButton.qml" line="64"/>
<location filename="qml/page/T_RadioButton.qml" line="115"/>
<location filename="qml/page/T_RadioButton.qml" line="166"/>
<source>Radio Button_1</source> <source>Radio Button_1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_RadioButton.qml" line="67"/> <location filename="qml/page/T_RadioButton.qml" line="67"/>
<location filename="qml/page/T_RadioButton.qml" line="118"/>
<location filename="qml/page/T_RadioButton.qml" line="169"/>
<source>Radio Button_2</source> <source>Radio Button_2</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_RadioButton.qml" line="71"/> <location filename="qml/page/T_RadioButton.qml" line="70"/>
<location filename="qml/page/T_RadioButton.qml" line="121"/>
<location filename="qml/page/T_RadioButton.qml" line="172"/>
<source>Radio Button_3</source> <source>Radio Button_3</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -1999,6 +2099,10 @@ Some contents...</source>
<source>ShortcutPicker</source> <source>ShortcutPicker</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Quit</source>
<translation type="obsolete">退</translation>
</message>
<message> <message>
<source>Activate the Shortcut</source> <source>Activate the Shortcut</source>
<translation type="obsolete"></translation> <translation type="obsolete"></translation>
@ -2089,88 +2193,88 @@ Some contents...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="73"/> <location filename="qml/page/T_TableView.qml" line="72"/>
<source>Modify the column name</source> <source>Modify the column name</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="74"/> <location filename="qml/page/T_TableView.qml" line="73"/>
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="92"/> <location filename="qml/page/T_TableView.qml" line="91"/>
<source>OK</source> <source>OK</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="134"/> <location filename="qml/page/T_TableView.qml" line="133"/>
<source>Search</source> <source>Search</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="177"/> <location filename="qml/page/T_TableView.qml" line="176"/>
<location filename="qml/page/T_TableView.qml" line="513"/> <location filename="qml/page/T_TableView.qml" line="508"/>
<source>Name</source> <source>Name</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="211"/> <location filename="qml/page/T_TableView.qml" line="210"/>
<source>Delete</source> <source>Delete</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="218"/> <location filename="qml/page/T_TableView.qml" line="217"/>
<source>Edit</source> <source>Edit</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="237"/> <location filename="qml/page/T_TableView.qml" line="236"/>
<source>Select All</source> <source>Select All</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="358"/> <location filename="qml/page/T_TableView.qml" line="364"/>
<source>Age</source> <source>Age</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="433"/> <location filename="qml/page/T_TableView.qml" line="439"/>
<source>Clear All</source> <source>Clear All</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="470"/> <location filename="qml/page/T_TableView.qml" line="475"/>
<source>Insert a Row</source> <source>Insert a Row</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="478"/> <location filename="qml/page/T_TableView.qml" line="482"/>
<source>Focus not acquired: Please click any item in the form as the target for insertion!</source> <source>Focus not acquired: Please click any item in the form as the target for insertion!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="508"/> <location filename="qml/page/T_TableView.qml" line="513"/>
<source>Avatar</source> <source>Avatar</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="526"/> <location filename="qml/page/T_TableView.qml" line="527"/>
<source>Address</source> <source>Address</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="534"/> <location filename="qml/page/T_TableView.qml" line="535"/>
<source>Nickname</source> <source>Nickname</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="541"/> <location filename="qml/page/T_TableView.qml" line="542"/>
<source>Long String</source> <source>Long String</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="549"/> <location filename="qml/page/T_TableView.qml" line="550"/>
<source>Options</source> <source>Options</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -2185,12 +2289,12 @@ Some contents...</source>
<translation type="unfinished">&gt;</translation> <translation type="unfinished">&gt;</translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="440"/> <location filename="qml/page/T_TableView.qml" line="446"/>
<source>Delete Selection</source> <source>Delete Selection</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_TableView.qml" line="464"/> <location filename="qml/page/T_TableView.qml" line="469"/>
<source>Add a row of Data</source> <source>Add a row of Data</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -2345,12 +2449,12 @@ Some contents...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_Timeline.qml" line="115"/> <location filename="qml/page/T_Timeline.qml" line="114"/>
<source>Append</source> <source>Append</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/page/T_Timeline.qml" line="121"/> <location filename="qml/page/T_Timeline.qml" line="120"/>
<source>clear</source> <source>clear</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>

View File

@ -43,7 +43,8 @@ FluLauncher {
"/singleTaskWindow":"qrc:/example/qml/window/SingleTaskWindow.qml", "/singleTaskWindow":"qrc:/example/qml/window/SingleTaskWindow.qml",
"/standardWindow":"qrc:/example/qml/window/StandardWindow.qml", "/standardWindow":"qrc:/example/qml/window/StandardWindow.qml",
"/singleInstanceWindow":"qrc:/example/qml/window/SingleInstanceWindow.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 var args = Qt.application.arguments
if(args.length>=2 && args[1].startsWith("-crashed=")){ if(args.length>=2 && args[1].startsWith("-crashed=")){
@ -52,4 +53,73 @@ FluLauncher {
FluRouter.navigate("/") 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})
}
}
}
} }

View File

@ -7,7 +7,9 @@ import "../component"
FluScrollablePage{ FluScrollablePage{
id: root
title: qsTr("Line Chart") title: qsTr("Line Chart")
property var data : []
FluFrame{ FluFrame{
Layout.preferredWidth: 500 Layout.preferredWidth: 500
@ -15,13 +17,14 @@ FluScrollablePage{
padding: 10 padding: 10
Layout.topMargin: 20 Layout.topMargin: 20
FluChart{ FluChart{
id: chart
anchors.fill: parent anchors.fill: parent
chartType: 'line' chartType: 'line'
chartData: { return { chartData: { return {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [{ datasets: [{
label: 'My First Dataset', label: 'My First Dataset',
data: [65, 59, 80, 81, 56, 55, 40], data: root.data,
fill: false, fill: false,
borderColor: 'rgb(75, 192, 192)', borderColor: 'rgb(75, 192, 192)',
tension: 0.1 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()
}
} }
} }

View File

@ -14,17 +14,8 @@ FluContentPage {
anchors{ anchors{
top: parent.top top: parent.top
} }
} onTextChanged: {
grid_view.model = FluApp.iconData(text_box.text)
FluFilledButton{
text: qsTr("Search")
anchors{
left: text_box.right
verticalCenter: text_box.verticalCenter
leftMargin: 14
}
onClicked: {
grid_view.model = FluApp.iconDatas(text_box.text)
} }
} }
GridView{ GridView{
@ -33,7 +24,7 @@ FluContentPage {
cellHeight: 110 cellHeight: 110
clip: true clip: true
boundsBehavior: GridView.StopAtBounds boundsBehavior: GridView.StopAtBounds
model: FluApp.iconDatas() model: FluApp.iconData()
ScrollBar.vertical: FluScrollBar {} ScrollBar.vertical: FluScrollBar {}
anchors{ anchors{
topMargin: 10 topMargin: 10

View File

@ -53,21 +53,20 @@ FluScrollablePage{
Layout.topMargin: 20 Layout.topMargin: 20
FluRadioButtons{ FluRadioButtons{
spacing: 8 spacing: 8
disabled: radio_button_switch2.checked
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors{ anchors{
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
left: parent.left left: parent.left
} }
currentIndex: 1
FluRadioButton{ FluRadioButton{
disabled: radio_button_switch2.checked
text: qsTr("Radio Button_1") text: qsTr("Radio Button_1")
} }
FluRadioButton{ FluRadioButton{
disabled: radio_button_switch2.checked
text: qsTr("Radio Button_2") text: qsTr("Radio Button_2")
} }
FluRadioButton{ FluRadioButton{
disabled: radio_button_switch2.checked
text: qsTr("Radio Button_3") 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"
}
}'
}
} }

View File

@ -11,12 +11,26 @@ FluScrollablePage{
FluFrame{ FluFrame{
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 100 Layout.preferredHeight: childrenRect.height
padding: 10 ColumnLayout{
FluShortcutPicker{
anchors.verticalCenter: parent.verticalCenter 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{ CodeExpander{
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: -6 Layout.topMargin: -6
@ -26,5 +40,3 @@ FluScrollablePage{
} }
} }

View File

@ -11,7 +11,6 @@ FluContentPage{
title: qsTr("TableView") title: qsTr("TableView")
signal checkBoxChanged signal checkBoxChanged
property var dataSource : []
property int sortType: 0 property int sortType: 0
property bool selectedAll: true property bool selectedAll: true
property string nameKeyword: "" property string nameKeyword: ""
@ -244,7 +243,9 @@ FluContentPage{
clickListener: function(){ clickListener: function(){
root.selectedAll = !root.selectedAll root.selectedAll = !root.selectedAll
var checked = 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++){ for(var i =0;i< table_view.rows ;i++){
var rowData = table_view.getRow(i) var rowData = table_view.getRow(i)
rowData.checkbox = table_view.customItem(com_checbox,{"checked":checked}) rowData.checkbox = table_view.customItem(com_checbox,{"checked":checked})
@ -271,7 +272,8 @@ FluContentPage{
} }
Component.onCompleted: { Component.onCompleted: {
currentIndex=["100","300","500","1000"].findIndex((element) => element === display) currentIndex=["100","300","500","1000"].findIndex((element) => element === display)
selectAll() textBox.forceActiveFocus()
textBox.selectAll()
} }
onCommit: { onCommit: {
editTextChaged(editText) editTextChaged(editText)
@ -293,6 +295,8 @@ FluContentPage{
}); });
items = result items = result
textbox.text= String(display) textbox.text= String(display)
forceActiveFocus()
selectAll()
} }
onCommit: { onCommit: {
editTextChaged(textbox.text) editTextChaged(textbox.text)
@ -344,7 +348,9 @@ FluContentPage{
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
custom_update_dialog.showDialog(options.title,function(text){ 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 data = []
var rows = [] var rows = []
for (var i = 0; i < table_view.rows; i++) { for (var i = 0; i < table_view.rows; i++) {
var item = table_view.getRow(i); var item = table_view.getRow(i)
rows.push(item) rows.push(item)
if (!item.checkbox.options.checked) { if (!item.checkbox.options.checked) {
data.push(item); data.push(item);
} }
} }
var sourceModel = table_view.sourceModel; var sourceModel = table_view.sourceModel
for (i = 0; i < sourceModel.rowCount; i++) { 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) const foundItem = rows.find(item=> item._key === sourceItem._key)
if (!foundItem) { if (!foundItem) {
data.push(sourceItem); data.push(sourceItem);
@ -459,7 +465,6 @@ FluContentPage{
table_view.dataSource = data table_view.dataSource = data
} }
} }
FluButton{ FluButton{
text: qsTr("Add a row of Data") text: qsTr("Add a row of Data")
onClicked: { onClicked: {
@ -469,18 +474,15 @@ FluContentPage{
FluButton{ FluButton{
text: qsTr("Insert a Row") text: qsTr("Insert a Row")
onClicked: { onClicked: {
if(typeof table_view.current !== 'undefined'){ var index = table_view.currentIndex()
var newLine = genTestObject() if(index !== -1){
var currentLine = dataSource.findIndex(obj => obj._key === table_view.current._key) var testObj = genTestObject()
root.dataSource.splice(currentLine, 0, newLine); table_view.insertRow(index,testObj)
table_view.dataSource = root.dataSource
}else{ }else{
showWarning(qsTr("Focus not acquired: Please click any item in the form as the target for insertion!")) 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}), title: table_view.customItem(com_column_checbox,{checked:true}),
dataIndex: 'checkbox', dataIndex: 'checkbox',
width:100, frozen: true
minimumWidth:100,
maximumWidth:100
},
{
title: table_view.customItem(com_column_update_title,{title:qsTr("Avatar")}),
dataIndex: 'avatar',
width:100
}, },
{ {
title: table_view.customItem(com_column_filter_name,{title:qsTr("Name")}), title: table_view.customItem(com_column_filter_name,{title:qsTr("Name")}),
dataIndex: 'name', dataIndex: 'name',
readOnly:true 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}), title: table_view.customItem(com_column_sort_age,{sort:0}),
dataIndex: 'age', dataIndex: 'age',
@ -549,8 +550,7 @@ FluContentPage{
title: qsTr("Options"), title: qsTr("Options"),
dataIndex: 'action', dataIndex: 'action',
width:160, width:160,
minimumWidth:160, frozen:true
maximumWidth:160
} }
] ]
} }
@ -620,7 +620,6 @@ FluContentPage{
for(var i=0;i<count;i++){ for(var i=0;i<count;i++){
dataSource.push(genTestObject()) dataSource.push(genTestObject())
} }
root.dataSource = dataSource table_view.dataSource = dataSource
table_view.dataSource = root.dataSource
} }
} }

View File

@ -44,7 +44,6 @@ FluScrollablePage{
wrapMode: Text.WrapAnywhere wrapMode: Text.WrapAnywhere
horizontalAlignment: isRight ? Qt.AlignRight : Qt.AlignLeft horizontalAlignment: isRight ? Qt.AlignRight : Qt.AlignLeft
text: modelData.text text: modelData.text
font.bold: true
linkColor: FluTheme.dark ? FluColors.Teal.lighter : FluColors.Teal.dark linkColor: FluTheme.dark ? FluColors.Teal.lighter : FluColors.Teal.dark
onLinkActivated: onLinkActivated:
(link)=> { (link)=> {

View File

@ -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
}
}

View File

@ -14,9 +14,9 @@ FluWindow {
id:window id:window
title: "FluentUI" title: "FluentUI"
width: 1000 width: 1000
height: 680 height: 668
minimumWidth: 680 minimumWidth: 668
minimumHeight: 200 minimumHeight: 320
launchMode: FluWindowType.SingleTask launchMode: FluWindowType.SingleTask
fitsAppBarWindows: true fitsAppBarWindows: true
appBar: FluAppBar { appBar: FluAppBar {

View File

@ -2,6 +2,7 @@
#include <QOpenGLFramebufferObjectFormat> #include <QOpenGLFramebufferObjectFormat>
#include <QOpenGLShaderProgram> #include <QOpenGLShaderProgram>
#include <QQuickWindow>
class FBORenderer : public QQuickFramebufferObject::Renderer, protected QOpenGLFunctions { class FBORenderer : public QQuickFramebufferObject::Renderer, protected QOpenGLFunctions {
public: public:
@ -47,6 +48,7 @@ QOpenGLFramebufferObject *FBORenderer::createFramebufferObject(const QSize &size
} }
void FBORenderer::render() { void FBORenderer::render() {
auto pixelRatio = item->window()->devicePixelRatio();
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@ -61,7 +63,7 @@ void FBORenderer::render() {
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
program.setAttributeArray(0, GL_FLOAT, values, 2); program.setAttributeArray(0, GL_FLOAT, values, 2);
program.setUniformValue("t", (float) item->t()); 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); glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE); glBlendFunc(GL_SRC_ALPHA, GL_ONE);

View File

@ -11,6 +11,13 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/.cmake/)
# #
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) 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) 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) set(FLUENTUI_QML_PLUGIN_DIRECTORY ${QT_SDK_DIR}/qml/FluentUI)
endif () 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_LUPDATE NAMES lupdate)
find_program(QT_LRELEASE NAMES lrelease) 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) file(GLOB_RECURSE CPP_FILES *.cpp *.h *.cxx)
foreach (filepath ${CPP_FILES}) foreach (filepath ${CPP_FILES})
string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath}) string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath})
message(${filename})
list(APPEND sources_files ${filename}) list(APPEND sources_files ${filename})
endforeach (filepath) 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") if (QT_VERSION VERSION_GREATER_EQUAL "6.2")
#fluentuiplugin.cppfluentuiplugin.hQt5使Qt6 #fluentuiplugin.cppfluentuiplugin.hQt5使Qt6
list(REMOVE_ITEM sources_files fluentuiplugin.h fluentuiplugin.cpp) 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}::Quick
Qt${QT_VERSION_MAJOR}::Qml 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) install(DIRECTORY ${FLUENTUI_QML_PLUGIN_DIRECTORY} DESTINATION ${CMAKE_INSTALL_PREFIX}/imports)

View File

@ -1,6 +1,5 @@
#include "FluApp.h" #include "FluApp.h"
#include <QQmlEngine>
#include <QGuiApplication> #include <QGuiApplication>
#include <QQuickItem> #include <QQuickItem>
#include <QTimer> #include <QTimer>
@ -17,9 +16,10 @@ FluApp::FluApp(QObject *parent) : QObject{parent} {
FluApp::~FluApp() = default; FluApp::~FluApp() = default;
void FluApp::init(QObject *target, QLocale locale) { void FluApp::init(QObject *launcher, QLocale locale) {
this->launcher(launcher);
_locale = std::move(locale); _locale = std::move(locale);
_engine = qmlEngine(target); _engine = qmlEngine(launcher);
_translator = new QTranslator(this); _translator = new QTranslator(this);
QGuiApplication::installTranslator(_translator); QGuiApplication::installTranslator(_translator);
const QStringList uiLanguages = _locale.uiLanguages(); 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; 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) { for (int i = 0; i <= enumType.keyCount() - 1; ++i) {
QString name = enumType.key(i); QString name = enumType.key(i);
int icon = enumType.value(i); int icon = enumType.value(i);

View File

@ -21,6 +21,7 @@ Q_OBJECT
Q_PROPERTY_AUTO(bool, useSystemAppBar) Q_PROPERTY_AUTO(bool, useSystemAppBar)
Q_PROPERTY_AUTO(QString, windowIcon) Q_PROPERTY_AUTO(QString, windowIcon)
Q_PROPERTY_AUTO(QLocale, locale) Q_PROPERTY_AUTO(QLocale, locale)
Q_PROPERTY_AUTO_P(QObject*,launcher)
QML_NAMED_ELEMENT(FluApp) QML_NAMED_ELEMENT(FluApp)
QML_SINGLETON QML_SINGLETON
@ -34,9 +35,9 @@ SINGLETON(FluApp)
static FluApp *create(QQmlEngine *, QJSEngine *) { return getInstance(); } 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: private:
QQmlEngine *_engine{}; QQmlEngine *_engine{};

View File

@ -7,6 +7,7 @@
#include "FluTools.h" #include "FluTools.h"
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#pragma comment (lib, "user32.lib") #pragma comment (lib, "user32.lib")
#pragma comment (lib, "dwmapi.lib") #pragma comment (lib, "dwmapi.lib")
@ -14,7 +15,6 @@
#include <windowsx.h> #include <windowsx.h>
#include <dwmapi.h> #include <dwmapi.h>
static inline QByteArray qtNativeEventType() { static inline QByteArray qtNativeEventType() {
static const auto result = "windows_generic_MSG"; static const auto result = "windows_generic_MSG";
return result; return result;
@ -35,15 +35,28 @@ static inline bool isCompositionEnabled() {
return false; 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<DwmExtendFrameIntoClientAreaPtr>(GetProcAddress(module, "DwmExtendFrameIntoClientArea"));
if (dwm_extendframe_into_client_area_) {
dwm_extendframe_into_client_area_(hwnd, &shadow);
}
}
}
#endif #endif
bool containsCursorToItem(QQuickItem *item) { bool containsCursorToItem(QQuickItem *item) {
if (!item || !item->isVisible()) { if (!item || !item->isVisible()) {
return false; return false;
} }
auto point = QCursor::pos(); auto point = item->window()->mapFromGlobal(QCursor::pos());
auto rect = QRectF(item->mapToGlobal(QPoint(0, 0)), item->size()); auto rect = QRectF(item->mapToItem(item->window()->contentItem(), QPointF(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())) { if (rect.contains(point)) {
return true; return true;
} }
return false; return false;
@ -73,7 +86,7 @@ void FluFrameless::componentComplete() {
int w = window()->width(); int w = window()->width();
int h = window()->height(); int h = window()->height();
_current = window()->winId(); _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) { if (!_fixSize) {
window()->setFlag(Qt::WindowMaximizeButtonHint); window()->setFlag(Qt::WindowMaximizeButtonHint);
} }
@ -92,25 +105,40 @@ void FluFrameless::componentComplete() {
HWND hwnd = reinterpret_cast<HWND>(window()->winId()); HWND hwnd = reinterpret_cast<HWND>(window()->winId());
DWORD style = ::GetWindowLongPtr(hwnd, GWL_STYLE); DWORD style = ::GetWindowLongPtr(hwnd, GWL_STYLE);
if (_fixSize) { 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) { for (int i = 0; i <= QGuiApplication::screens().count() - 1; ++i) {
connect(QGuiApplication::screens().at(i), &QScreen::logicalDotsPerInchChanged, this, [=] { connect(QGuiApplication::screens().at(i), &QScreen::logicalDotsPerInchChanged, this, [=] {
SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_FRAMECHANGED); SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_FRAMECHANGED);
}); });
} }
} else { } 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); SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
connect(window(), &QQuickWindow::screenChanged, this, [hwnd] { connect(window(), &QQuickWindow::screenChanged, this, [hwnd] {
::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOOWNERZORDER); ::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); ::RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
}); });
if (!window()->property("_hideShadow").toBool()) {
setShadow(hwnd);
}
#endif #endif
h = qRound(h + _appbar->height()); auto appBarHeight = _appbar->height();
h = qRound(h + appBarHeight);
if (_fixSize) { if (_fixSize) {
window()->setMaximumSize(QSize(w, h)); window()->setMaximumSize(QSize(w, h));
window()->setMinimumSize(QSize(w, h)); window()->setMinimumSize(QSize(w, h));
} else {
window()->setMinimumHeight(window()->minimumHeight() + appBarHeight);
window()->setMaximumHeight(window()->maximumHeight() + appBarHeight);
} }
window()->resize(QSize(w, h)); window()->resize(QSize(w, h));
connect(this, &FluFrameless::topmostChanged, this, [this] { connect(this, &FluFrameless::topmostChanged, this, [this] {
@ -144,24 +172,39 @@ void FluFrameless::componentComplete() {
return true; return true;
} }
return false; return false;
} else if (uMsg == WM_NCCALCSIZE) { } else if (uMsg == WM_NCCALCSIZE && wParam == TRUE) {
const auto clientRect = ((wParam == FALSE) ? reinterpret_cast<LPRECT>(lParam) : &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam))->rgrc[0]); const auto clientRect = &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam))->rgrc[0];
bool isMaximum = ::IsZoomed(hwnd); const LONG originalTop = clientRect->top;
if (!isMaximum){ const LONG originalLeft = clientRect->left;
if (clientRect->top != 0) const LONG originalBottom = clientRect->bottom;
{ const LONG originalRight = clientRect->right;
clientRect->top -= 1; const LRESULT hitTestResult = ::DefWindowProcW(hwnd, WM_NCCALCSIZE, wParam, lParam);
clientRect->bottom -= 1; if ((hitTestResult != HTERROR) && (hitTestResult != HTNOWHERE)) {
} *result = static_cast<QT_NATIVE_EVENT_RESULT_TYPE>(hitTestResult);
} else{ return true;
const LONG originalTop = clientRect->top;
const LRESULT hitTestResult = ::DefWindowProcW(hwnd, WM_NCCALCSIZE, wParam, lParam);
if ((hitTestResult != HTERROR) && (hitTestResult != HTNOWHERE)) {
*result = static_cast<QT_NATIVE_EVENT_RESULT_TYPE>(hitTestResult);
return true;
}
clientRect->top = originalTop-originalTop;
} }
#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); _setMaximizeHovered(false);
*result = WVR_REDRAW; *result = WVR_REDRAW;
return true; return true;
@ -219,17 +262,32 @@ void FluFrameless::componentComplete() {
*result = HTCLIENT; *result = HTCLIENT;
return true; return true;
} else if (uMsg == WM_NCPAINT) { } 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; return false;
} }
*result = FALSE; *result = FALSE;
return true; return true;
#endif
} else if (uMsg == WM_NCACTIVATE) { } else if (uMsg == WM_NCACTIVATE) {
if(isCompositionEnabled()){
return false;
}
*result = TRUE; *result = TRUE;
return 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<MINMAXINFO *>(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)) { } else if (_isWindows11OrGreater && (uMsg == WM_NCLBUTTONDBLCLK || uMsg == WM_NCLBUTTONDOWN)) {
if (_hitMaximizeButton()) { if (_hitMaximizeButton()) {
QMouseEvent event = QMouseEvent(QEvent::MouseButtonPress, QPoint(), QPoint(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QMouseEvent event = QMouseEvent(QEvent::MouseButtonPress, QPoint(), QPoint(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
@ -244,11 +302,11 @@ void FluFrameless::componentComplete() {
_setMaximizePressed(false); _setMaximizePressed(false);
return true; return true;
} }
} else if (uMsg == WM_ERASEBKGND) {
return true;
} else if (uMsg == WM_NCRBUTTONDOWN) { } else if (uMsg == WM_NCRBUTTONDOWN) {
if (wParam == HTCAPTION) { 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) { } else if (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN) {
const bool altPressed = ((wParam == VK_MENU) || (::GetKeyState(VK_MENU) < 0)); const bool altPressed = ((wParam == VK_MENU) || (::GetKeyState(VK_MENU) < 0));
@ -286,9 +344,16 @@ bool FluFrameless::_isFullScreen() {
void FluFrameless::_showSystemMenu(QPoint point) { void FluFrameless::_showSystemMenu(QPoint point) {
#ifdef Q_OS_WIN #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<HWND>(window()->winId()); HWND hwnd = reinterpret_cast<HWND>(window()->winId());
DWORD style = ::GetWindowLongPtr(hwnd, GWL_STYLE);
::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_SYSMENU);
auto hMenu = ::GetSystemMenu(hwnd, FALSE); auto hMenu = ::GetSystemMenu(hwnd, FALSE);
if (_isMaximized() || _isFullScreen()) { if (_isMaximized() || _isFullScreen()) {
::EnableMenuItem(hMenu, SC_MOVE, MFS_DISABLED); ::EnableMenuItem(hMenu, SC_MOVE, MFS_DISABLED);
@ -304,12 +369,11 @@ void FluFrameless::_showSystemMenu(QPoint point) {
::EnableMenuItem(hMenu, SC_SIZE, MFS_DISABLED); ::EnableMenuItem(hMenu, SC_SIZE, MFS_DISABLED);
::EnableMenuItem(hMenu, SC_MAXIMIZE, 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()), const int result = ::TrackPopupMenu(hMenu, (TPM_RETURNCMD | (QGuiApplication::isRightToLeft() ? TPM_RIGHTALIGN : TPM_LEFTALIGN)), nativePos.x(),
qRound(point.y() * window()->devicePixelRatio()), 0, hwnd, nullptr); nativePos.y(), 0, hwnd, nullptr);
if (result != FALSE) { if (result) {
::PostMessageW(hwnd, WM_SYSCOMMAND, result, 0); ::PostMessageW(hwnd, WM_SYSCOMMAND, result, 0);
} }
::SetWindowLongPtr(hwnd, GWL_STYLE, style & ~WS_SYSMENU);
#endif #endif
} }
@ -334,36 +398,40 @@ bool FluFrameless::_hitMaximizeButton() {
} }
void FluFrameless::_setMaximizePressed(bool val) { void FluFrameless::_setMaximizePressed(bool val) {
_maximizeButton->setProperty("down", val); if (_maximizeButton) {
_maximizeButton->setProperty("down", val);
}
} }
void FluFrameless::_setMaximizeHovered(bool val) { void FluFrameless::_setMaximizeHovered(bool val) {
_maximizeButton->setProperty("hover", val); if (_maximizeButton) {
_maximizeButton->setProperty("hover", val);
}
} }
void FluFrameless::_updateCursor(int edges) { void FluFrameless::_updateCursor(int edges) {
switch (edges) { switch (edges) {
case 0: case 0:
window()->setCursor(Qt::ArrowCursor); window()->setCursor(Qt::ArrowCursor);
break; break;
case Qt::LeftEdge: case Qt::LeftEdge:
case Qt::RightEdge: case Qt::RightEdge:
window()->setCursor(Qt::SizeHorCursor); window()->setCursor(Qt::SizeHorCursor);
break; break;
case Qt::TopEdge: case Qt::TopEdge:
case Qt::BottomEdge: case Qt::BottomEdge:
window()->setCursor(Qt::SizeVerCursor); window()->setCursor(Qt::SizeVerCursor);
break; break;
case Qt::LeftEdge | Qt::TopEdge: case Qt::LeftEdge | Qt::TopEdge:
case Qt::RightEdge | Qt::BottomEdge: case Qt::RightEdge | Qt::BottomEdge:
window()->setCursor(Qt::SizeFDiagCursor); window()->setCursor(Qt::SizeFDiagCursor);
break; break;
case Qt::RightEdge | Qt::TopEdge: case Qt::RightEdge | Qt::TopEdge:
case Qt::LeftEdge | Qt::BottomEdge: case Qt::LeftEdge | Qt::BottomEdge:
window()->setCursor(Qt::SizeBDiagCursor); window()->setCursor(Qt::SizeBDiagCursor);
break; break;
default: default:
break; break;
} }
} }

32
src/FluHotkey.cpp Normal file
View File

@ -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;
}
}

24
src/FluHotkey.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef FLUHOTKEY_H
#define FLUHOTKEY_H
#include <QObject>
#include <QQuickItem>
#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

63
src/FluTableModel.cpp Normal file
View File

@ -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<int, QByteArray> 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);
}

46
src/FluTableModel.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef FLUTABLEMODEL_H
#define FLUTABLEMODEL_H
#include <QObject>
#include <QAbstractItemModel>
#include <QtQml/qqml.h>
#include "stdafx.h"
class FluTableModel : public QAbstractTableModel {
Q_OBJECT
Q_PROPERTY_AUTO(QList<QVariantMap>, columnSource)
Q_PROPERTY_AUTO(QList<QVariantMap>, 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<int, QByteArray> 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

View File

@ -3,9 +3,8 @@
#include <QJSValueList> #include <QJSValueList>
FluTableSortProxyModel::FluTableSortProxyModel(QSortFilterProxyModel *parent) : QSortFilterProxyModel{parent} { FluTableSortProxyModel::FluTableSortProxyModel(QSortFilterProxyModel *parent) : QSortFilterProxyModel{parent} {
_model = nullptr;
connect(this, &FluTableSortProxyModel::modelChanged, this, [=] { connect(this, &FluTableSortProxyModel::modelChanged, this, [=] {
setSourceModel(this->model()); setSourceModel(this->model().value<QAbstractTableModel *>());
}); });
} }
@ -59,15 +58,18 @@ bool FluTableSortProxyModel::lessThan(const QModelIndex &source_left, const QMod
[[maybe_unused]] QVariant FluTableSortProxyModel::getRow(int rowIndex) { [[maybe_unused]] QVariant FluTableSortProxyModel::getRow(int rowIndex) {
QVariant result; QVariant result;
QMetaObject::invokeMethod(_model, "getRow", Q_RETURN_ARG(QVariant, result), Q_ARG(int, mapToSource(index(rowIndex, 0)).row())); QMetaObject::invokeMethod(_model.value<QAbstractTableModel *>(), "getRow", Q_RETURN_ARG(QVariant, result), Q_ARG(int, mapToSource(index(rowIndex, 0)).row()));
return result; return result;
} }
[[maybe_unused]] void FluTableSortProxyModel::setRow(int rowIndex, const QVariant &val) { [[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<QAbstractTableModel *>(), "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<QAbstractTableModel *>(), "insertRow", Q_ARG(int, mapToSource(index(rowIndex, 0)).row()), Q_ARG(QVariant, val));
} }
[[maybe_unused]] void FluTableSortProxyModel::removeRow(int rowIndex, int rows) { [[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<QAbstractTableModel *>(), "removeRow", Q_ARG(int, mapToSource(index(rowIndex, 0)).row()), Q_ARG(int, rows));
} }

View File

@ -8,7 +8,7 @@
class FluTableSortProxyModel : public QSortFilterProxyModel { class FluTableSortProxyModel : public QSortFilterProxyModel {
Q_OBJECT Q_OBJECT
Q_PROPERTY_AUTO_P(QAbstractTableModel*, model) Q_PROPERTY_AUTO_P(QVariant, model)
QML_NAMED_ELEMENT(FluTableSortProxyModel) QML_NAMED_ELEMENT(FluTableSortProxyModel)
public: public:
explicit FluTableSortProxyModel(QSortFilterProxyModel *parent = nullptr); 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 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 removeRow(int rowIndex, int rows);
[[maybe_unused]] Q_INVOKABLE void setComparator(const QJSValue &comparator); [[maybe_unused]] Q_INVOKABLE void setComparator(const QJSValue &comparator);

View File

@ -95,5 +95,5 @@ SINGLETON(FluTools)
Q_INVOKABLE QString getWallpaperFilePath(); Q_INVOKABLE QString getWallpaperFilePath();
Q_INVOKABLE QColor imageMainColor(const QImage& image, double bright = 1); Q_INVOKABLE QColor imageMainColor(const QImage &image, double bright = 1);
}; };

View File

@ -1,25 +1,14 @@
#include "FluTreeModel.h" #include "FluTreeModel.h"
#include <QMetaEnum> #include <QMetaEnum>
#include <utility>
FluTreeNode::FluTreeNode(QObject *parent) : QObject{parent} { FluTreeNode::FluTreeNode(QObject *parent) : QObject{parent} {
} }
FluTreeModel::FluTreeModel(QObject *parent) : QAbstractItemModel{parent} { FluTreeModel::FluTreeModel(QObject *parent) : QAbstractTableModel{parent} {
_dataSourceSize = 0; _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 { int FluTreeModel::rowCount(const QModelIndex &parent) const {
return _rows.count(); return _rows.count();
} }
@ -265,12 +254,12 @@ void FluTreeModel::allCollapse() {
endResetModel(); endResetModel();
} }
QVariant FluTreeModel::selectionModel(){ QVariant FluTreeModel::selectionModel() {
QList<FluTreeNode *> data; QList<FluTreeNode *> data;
foreach (auto item, _dataSource) { foreach (auto item, _dataSource) {
if (item->checked()) { if (item->checked()) {
data.append(item); data.append(item);
}
} }
}
return QVariant::fromValue(data); return QVariant::fromValue(data);
} }

View File

@ -50,7 +50,7 @@ public:
} }
return true; return true;
}; };
Q_INVOKABLE bool hideLineFooter() { Q_INVOKABLE bool hideLineFooter() {
if (_parent) { if (_parent) {
auto childIndex = _parent->_children.indexOf(this); auto childIndex = _parent->_children.indexOf(this);
@ -86,12 +86,11 @@ public:
FluTreeNode *_parent = nullptr; FluTreeNode *_parent = nullptr;
}; };
class FluTreeModel : public QAbstractItemModel { class FluTreeModel : public QAbstractTableModel {
Q_OBJECT Q_OBJECT
Q_PROPERTY_AUTO(int, dataSourceSize) Q_PROPERTY_AUTO(int, dataSourceSize)
Q_PROPERTY_AUTO(QList<QVariantMap>, columnSource) Q_PROPERTY_AUTO(QList<QVariantMap>, columnSource)
QML_NAMED_ELEMENT(FluTreeModel) QML_NAMED_ELEMENT(FluTreeModel)
QML_ADDED_IN_MINOR_VERSION(1)
public: public:
enum TreeModelRoles { enum TreeModelRoles {
RowModel = 0x0101, RowModel = 0x0101,
@ -108,10 +107,6 @@ public:
[[nodiscard]] QHash<int, QByteArray> roleNames() const override; [[nodiscard]] QHash<int, QByteArray> 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 removeRows(int row, int count);
Q_INVOKABLE void insertRows(int row, const QList<FluTreeNode *> &data); Q_INVOKABLE void insertRows(int row, const QList<FluTreeNode *> &data);

View File

@ -3,9 +3,9 @@
#include <QObject> #include <QObject>
#include <QtQml/qqml.h> #include <QtQml/qqml.h>
namespace Fluent_Icons { namespace FluentIcons {
Q_NAMESPACE Q_NAMESPACE
enum class Fluent_IconType { enum class Type {
GlobalNavButton = 0xe700, GlobalNavButton = 0xe700,
Wifi = 0xe701, Wifi = 0xe701,
Bluetooth = 0xe702, Bluetooth = 0xe702,
@ -1411,7 +1411,7 @@ namespace Fluent_Icons {
ClickedOutLoudSolidBold = 0xf8b3 ClickedOutLoudSolidBold = 0xf8b3
}; };
Q_ENUM_NS(Fluent_IconType) Q_ENUM_NS(Type)
QML_NAMED_ELEMENT(FluentIcons) QML_NAMED_ELEMENT(FluentIcons)
} }

View File

@ -15,6 +15,8 @@
#include "FluQrCodeItem.h" #include "FluQrCodeItem.h"
#include "FluTableSortProxyModel.h" #include "FluTableSortProxyModel.h"
#include "FluFrameless.h" #include "FluFrameless.h"
#include "FluTableModel.h"
#include "FluHotkey.h"
void FluentUI::registerTypes(QQmlEngine *engine) { void FluentUI::registerTypes(QQmlEngine *engine) {
initializeEngine(engine, _uri); initializeEngine(engine, _uri);
@ -32,8 +34,10 @@ void FluentUI::registerTypes(const char *uri) const {
qmlRegisterType<FluWatermark>(uri, major, minor, "FluWatermark"); qmlRegisterType<FluWatermark>(uri, major, minor, "FluWatermark");
qmlRegisterType<FluAccentColor>(uri, major, minor, "FluAccentColor"); qmlRegisterType<FluAccentColor>(uri, major, minor, "FluAccentColor");
qmlRegisterType<FluTreeModel>(uri, major, minor, "FluTreeModel"); qmlRegisterType<FluTreeModel>(uri, major, minor, "FluTreeModel");
qmlRegisterType<FluTableModel>(uri, major, minor, "FluTableModel");
qmlRegisterType<FluRectangle>(uri, major, minor, "FluRectangle"); qmlRegisterType<FluRectangle>(uri, major, minor, "FluRectangle");
qmlRegisterType<FluFrameless>(uri, major, minor, "FluFrameless"); qmlRegisterType<FluFrameless>(uri, major, minor, "FluFrameless");
qmlRegisterType<FluHotkey>(uri, major, minor, "FluHotkey");
qmlRegisterType<FluTableSortProxyModel>(uri, major, minor, "FluTableSortProxyModel"); qmlRegisterType<FluTableSortProxyModel>(uri, major, minor, "FluTableSortProxyModel");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluAcrylic.qml"), uri, major, minor, "FluAcrylic"); 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/FluRouter.qml"), uri, major, minor, "FluRouter");
qmlRegisterSingletonType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluEventBus.qml"), uri, major, minor, "FluEventBus"); 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(FluThemeType::staticMetaObject, uri, major, minor, "FluThemeType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluPageType::staticMetaObject, uri, major, minor, "FluPageType", "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"); 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(FluTimelineType::staticMetaObject, uri, major, minor, "FluTimelineType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluSheetType::staticMetaObject, uri, major, minor, "FluSheetType", "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); qmlRegisterModule(uri, major, minor);
#endif #endif
} }
void FluentUI::initializeEngine(QQmlEngine *engine, [[maybe_unused]] const char *uri) { void FluentUI::initializeEngine(QQmlEngine *engine, [[maybe_unused]] const char *uri) {
engine->rootContext()->setContextProperty("FluApp", FluApp::getInstance()); Q_UNUSED(engine)
engine->rootContext()->setContextProperty("FluColors", FluColors::getInstance());
engine->rootContext()->setContextProperty("FluTheme", FluTheme::getInstance());
engine->rootContext()->setContextProperty("FluTools", FluTools::getInstance());
engine->rootContext()->setContextProperty("FluTextStyle", FluTextStyle::getInstance());
} }

View File

@ -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 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 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 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, implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding) implicitContentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
@ -39,6 +40,7 @@ T.ComboBox {
opacity: enabled ? 1 : 0.3 opacity: enabled ? 1 : 0.3
} }
contentItem: T.TextField { contentItem: T.TextField {
id: text_field
property bool disabled: !control.editable property bool disabled: !control.editable
leftPadding: !control.mirrored ? 10 : control.editable && activeFocus ? 3 : 1 leftPadding: !control.mirrored ? 10 : control.editable && activeFocus ? 3 : 1
rightPadding: control.mirrored ? 10 : control.editable && activeFocus ? 3 : 1 rightPadding: control.mirrored ? 10 : control.editable && activeFocus ? 3 : 1

View File

@ -3,45 +3,40 @@ import QtQuick.Controls 2.15
import FluentUI 1.0 import FluentUI 1.0
FluObject { FluObject {
property var root; property var root
property int layoutY: 75 property int layoutY: 75
id:control id:control
FluObject{ FluObject{
id:mcontrol id:mcontrol
property string const_success: "success"; property string const_success: "success"
property string const_info: "info"; property string const_info: "info"
property string const_warning: "warning"; property string const_warning: "warning"
property string const_error: "error"; property string const_error: "error"
property int maxWidth: 300; property int maxWidth: 300
property var screenLayout: null; property var screenLayout: null
function create(type,text,duration,moremsg){ function create(type,text,duration,moremsg){
if(screenLayout){ if(screenLayout){
var last = screenLayout.getLastloader(); var last = screenLayout.getLastloader()
if(last.type === type && last.text === text && moremsg === last.moremsg){ if(last.type === type && last.text === text && moremsg === last.moremsg){
last.duration = duration last.duration = duration
if (duration > 0) last.restart(); if (duration > 0) last.restart()
return last; return last
} }
} }
initScreenLayout(); initScreenLayout()
return contentComponent.createObject(screenLayout,{ return contentComponent.createObject(screenLayout,{type:type,text:text,duration:duration,moremsg:moremsg,})
type:type,
text:text,
duration:duration,
moremsg:moremsg,
});
} }
function createCustom(itemcomponent,duration){ function createCustom(itemcomponent,duration){
initScreenLayout(); initScreenLayout()
if(itemcomponent){ if(itemcomponent){
return contentComponent.createObject(screenLayout,{itemcomponent:itemcomponent,duration:duration}); return contentComponent.createObject(screenLayout,{itemcomponent:itemcomponent,duration:duration})
} }
} }
function initScreenLayout(){ function initScreenLayout(){
if(screenLayout == null){ if(screenLayout == null){
screenLayout = screenlayoutComponent.createObject(root); screenLayout = screenlayoutComponent.createObject(root)
screenLayout.y = control.layoutY; screenLayout.y = control.layoutY
screenLayout.z = 100000; screenLayout.z = 100000
} }
} }
Component{ Component{
@ -58,44 +53,44 @@ FluObject {
duration: FluTheme.animationEnabled ? 333 : 0 duration: FluTheme.animationEnabled ? 333 : 0
} }
} }
onChildrenChanged: if(children.length === 0) destroy(); onChildrenChanged: if(children.length === 0) destroy()
function getLastloader(){ function getLastloader(){
if(children.length > 0){ if(children.length > 0){
return children[children.length - 1]; return children[children.length - 1]
} }
return null; return null
} }
} }
} }
Component{ Component{
id:contentComponent id:contentComponent
Item{ Item{
id:content; id:content
property int duration: 1500 property int duration: 1500
property var itemcomponent property var itemcomponent
property string type property string type
property string text property string text
property string moremsg property string moremsg
width: parent.width; width: parent.width
height: loader.height; height: loader.height
function close(){ function close(){
content.destroy(); content.destroy()
} }
function restart(){ function restart(){
delayTimer.restart(); delayTimer.restart()
} }
Timer { Timer {
id:delayTimer id:delayTimer
interval: duration; interval: duration
running: duration > 0; running: duration > 0
repeat: duration > 0 repeat: duration > 0
onTriggered: content.close(); onTriggered: content.close()
} }
FluLoader{ FluLoader{
id:loader; id:loader
x:(parent.width - width) / 2; x:(parent.width - width) / 2
property var _super: content; property var _super: content
scale: item ? 1 : 0; scale: item ? 1 : 0
asynchronous: true asynchronous: true
Behavior on scale { Behavior on scale {
enabled: FluTheme.animationEnabled enabled: FluTheme.animationEnabled
@ -104,30 +99,30 @@ FluObject {
duration: 167 duration: 167
} }
} }
sourceComponent:itemcomponent ? itemcomponent : mcontrol.fluent_sytle; sourceComponent:itemcomponent ? itemcomponent : mcontrol.fluent_sytle
} }
} }
} }
property Component fluent_sytle: Rectangle{ property Component fluent_sytle: Rectangle{
width: rowlayout.width + (btn_close.visible ? 30 : 48); width: rowlayout.width + (btn_close.visible ? 30 : 48)
height: rowlayout.height + 20; height: rowlayout.height + 20
color: { color: {
if(FluTheme.dark){ if(FluTheme.dark){
switch(_super.type){ switch(_super.type){
case mcontrol.const_success: return Qt.rgba(57/255,61/255,27/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_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_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_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{ }else{
switch(_super.type){ switch(_super.type){
case mcontrol.const_success: return "#dff6dd"; case mcontrol.const_success: return Qt.rgba(223/255,246/255,221/255,1)
case mcontrol.const_warning: return "#fff4ce"; case mcontrol.const_warning: return Qt.rgba(255/255,244/255,206/255,1)
case mcontrol.const_info: return "#f4f4f4"; case mcontrol.const_info: return Qt.rgba(244/255,244/255,244/255,1)
case mcontrol.const_error: return "#fde7e9"; case mcontrol.const_error: return Qt.rgba(253/255,231/255,233/255,1)
} }
return "#FFFFFF" return Qt.rgba(1,1,1,1)
} }
} }
FluShadow{ FluShadow{
@ -138,34 +133,34 @@ FluObject {
border.color: { border.color: {
if(FluTheme.dark){ if(FluTheme.dark){
switch(_super.type){ switch(_super.type){
case mcontrol.const_success: return Qt.rgba(56/255,61/255,27/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_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_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_error: return Qt.rgba(67/255,39/255,38/255,1)
} }
return "#FFFFFF" return Qt.rgba(1,1,1,1)
}else{ }else{
switch(_super.type){ switch(_super.type){
case mcontrol.const_success: return "#d2e8d0"; case mcontrol.const_success: return Qt.rgba(210/255,232/255,208/255,1)
case mcontrol.const_warning: return "#f0e6c2"; case mcontrol.const_warning: return Qt.rgba(240/255,230/255,194/255,1)
case mcontrol.const_info: return "#e6e6e6"; case mcontrol.const_info: return Qt.rgba(230/255,230/255,230/255,1)
case mcontrol.const_error: return "#eed9db"; case mcontrol.const_error: return Qt.rgba(238/255,217/255,219/255,1)
} }
return "#FFFFFF" return Qt.rgba(1,1,1,1)
} }
} }
Row{ Row{
id:rowlayout id:rowlayout
x:20; x:20
y:(parent.height - height) / 2; y:(parent.height - height) / 2
spacing: 10 spacing: 10
FluIcon{ FluIcon{
iconSource:{ iconSource:{
switch(_super.type){ switch(_super.type){
case mcontrol.const_success: return FluentIcons.CompletedSolid; case mcontrol.const_success: return FluentIcons.CompletedSolid
case mcontrol.const_warning: return FluentIcons.InfoSolid; case mcontrol.const_warning: return FluentIcons.InfoSolid
case mcontrol.const_info: return FluentIcons.InfoSolid; case mcontrol.const_info: return FluentIcons.InfoSolid
case mcontrol.const_error: return FluentIcons.StatusErrorFull; case mcontrol.const_error: return FluentIcons.StatusErrorFull
}FluentIcons.StatusErrorFull }FluentIcons.StatusErrorFull
return FluentIcons.FA_info_circle return FluentIcons.FA_info_circle
} }
@ -173,20 +168,20 @@ FluObject {
iconColor: { iconColor: {
if(FluTheme.dark){ if(FluTheme.dark){
switch(_super.type){ switch(_super.type){
case mcontrol.const_success: return Qt.rgba(108/255,203/255,95/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_warning: return Qt.rgba(252/255,225/255,0/255,1)
case mcontrol.const_info: return FluTheme.primaryColor; case mcontrol.const_info: return FluTheme.primaryColor
case mcontrol.const_error: return Qt.rgba(255/255,153/255,164/255,1); case mcontrol.const_error: return Qt.rgba(255/255,153/255,164/255,1)
} }
return "#FFFFFF" return Qt.rgba(1,1,1,1)
}else{ }else{
switch(_super.type){ switch(_super.type){
case mcontrol.const_success: return "#0f7b0f"; case mcontrol.const_success: return Qt.rgba(15/255,123/255,15/255,1)
case mcontrol.const_warning: return "#9d5d00"; case mcontrol.const_warning: return Qt.rgba(157/255,93/255,0/255,1)
case mcontrol.const_info: return "#0066b4"; case mcontrol.const_info: return Qt.rgba(0/255,102/255,180/255,1)
case mcontrol.const_error: return "#c42b1c"; 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 id:btn_close
iconSource: FluentIcons.ChromeClose iconSource: FluentIcons.ChromeClose
iconSize: 10 iconSize: 10
y:5 verticalPadding: 0
horizontalPadding: 0
width: 30
height: 20
visible: _super.duration<=0 visible: _super.duration<=0
iconColor: { anchors.verticalCenter: parent.verticalCenter
if(FluTheme.dark){ iconColor: FluTheme.dark ? Qt.rgba(222/255,222/255,222/255,1) : Qt.rgba(97/255,97/255,97/255,1)
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"
}
}
onClicked: _super.close() onClicked: _super.close()
} }
} }
} }
} }
function showSuccess(text,duration=1000,moremsg){ 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){ 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){ 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){ 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){ function showCustom(itemcomponent,duration=1000){
return mcontrol.createCustom(itemcomponent,duration); return mcontrol.createCustom(itemcomponent,duration)
} }
function clearAllInfo(){ function clearAllInfo(){
if(mcontrol.screenLayout != null) { if(mcontrol.screenLayout != null) {

View File

@ -39,7 +39,7 @@ T.Menu {
: false : false
clip: true clip: true
currentIndex: control.currentIndex currentIndex: control.currentIndex
ScrollIndicator.vertical: ScrollIndicator {} ScrollBar.vertical: FluScrollBar{}
} }
background: Rectangle { background: Rectangle {
implicitWidth: 150 implicitWidth: 150

View File

@ -1120,7 +1120,8 @@ Item {
} }
padding: 0 padding: 0
focus: true focus: true
contentItem: Item{ contentItem: FluClip{
radius: [5,5,5,5]
ListView{ ListView{
id:list_view id:list_view
anchors.fill: parent anchors.fill: parent
@ -1145,7 +1146,6 @@ Item {
visible: item_button.activeFocus visible: item_button.activeFocus
radius:4 radius:4
} }
FluLoader{ FluLoader{
id:item_dot_loader id:item_dot_loader
anchors{ anchors{
@ -1160,7 +1160,6 @@ Item {
return undefined return undefined
} }
} }
} }
contentItem: FluText{ contentItem: FluText{
text:modelData.title text:modelData.title
@ -1187,13 +1186,13 @@ Item {
} }
} }
} }
background: FluRectangle{ background: Rectangle{
implicitWidth: 180 implicitWidth: 180
radius: [4,4,4,4] color:FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(252/255,252/255,252/255,1)
FluShadow{ border.color: FluTheme.dark ? Qt.rgba(26/255,26/255,26/255,1) : Qt.rgba(191/255,191/255,191/255,1)
radius: 4 border.width: 1
} radius: 5
color: FluTheme.dark ? Qt.rgba(51/255,48/255,48/255,1) : Qt.rgba(248/255,250/255,253/255,1) FluShadow{}
} }
function showPopup(pos,height,model){ function showPopup(pos,height,model){
background.implicitHeight = height background.implicitHeight = height

View File

@ -38,7 +38,6 @@ Item {
} }
Row { Row {
spacing: 5 spacing: 5
FluToggleButton { FluToggleButton {
property int pageNumber: 1 property int pageNumber: 1
visible: control.pageCount > 0 visible: control.pageCount > 0
@ -98,7 +97,6 @@ Item {
sourceComponent: footer sourceComponent: footer
} }
} }
function calcNewPage(page) { function calcNewPage(page) {
if (!page) if (!page)
return return
@ -108,5 +106,4 @@ Item {
control.pageCurrent = page_num control.pageCurrent = page_num
control.requestPage(page_num, control.__itemPerPage) control.requestPage(page_num, control.__itemPerPage)
} }
} }

View File

@ -3,44 +3,87 @@ import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import FluentUI 1.0 import FluentUI 1.0
ColumnLayout { Item{
default property alias buttons: control.data id:control
default property list<QtObject> buttons
property int currentIndex : -1 property int currentIndex : -1
property int spacing: 8
property int orientation: Qt.Vertical
property bool disabled: false property bool disabled: false
property bool manuallyDisabled: false property bool manuallyDisabled: false
id:control QtObject{
onCurrentIndexChanged: { id: d
for(var i = 0;i<buttons.length;i++){ function updateChecked(){
buttons[i].checked = false if(buttons.length === 0){
} return
var button = buttons[currentIndex] }
if(button){ for(var i = 0;i<buttons.length;i++){
button.checked = true buttons[i].checked = false
} }
} if(currentIndex>=0 && currentIndex<buttons.length){
onDisabledChanged: { buttons[currentIndex].checked = true
refreshButtonStatus()
}
onManuallyDisabledChanged: {
refreshButtonStatus()
}
Component.onCompleted: {
for(var i = 0;i<buttons.length;i++){
buttons[i].clickListener = function(){
for(var i = 0;i<buttons.length;i++){
var button = buttons[i]
if(this === button){
currentIndex = i
}
}
} }
} }
refreshButtonStatus() function refreshButtonStatus() {
} for(var i = 0;i<buttons.length;i++){
if(!manuallyDisabled) buttons[i].enabled = !disabled
function refreshButtonStatus() { }
for(var i = 0;i<buttons.length;i++){
if(!manuallyDisabled) buttons[i].enabled = !disabled
} }
} }
implicitWidth: childrenRect.width
implicitHeight: childrenRect.height
onCurrentIndexChanged: {
d.updateChecked()
}
onDisabledChanged: {
d.refreshButtonStatus()
}
onManuallyDisabledChanged: {
d.refreshButtonStatus()
}
Component{
id:com_vertical
ColumnLayout {
data: control.buttons
spacing: control.spacing
Component.onCompleted: {
for(var i = 0;i<control.buttons.length;i++){
control.buttons[i].clickListener = function(){
for(var i = 0;i<control.buttons.length;i++){
var button = control.buttons[i]
if(this === button){
control.currentIndex = i
}
}
}
}
d.updateChecked()
d.refreshButtonStatus()
}
}
}
Component{
id:com_horizontal
RowLayout {
data: control.buttons
spacing: control.spacing
Component.onCompleted: {
for(var i = 0;i<control.buttons.length;i++){
control.buttons[i].clickListener = function(){
for(var i = 0;i<control.buttons.length;i++){
var button = control.buttons[i]
if(this === button){
control.currentIndex = i
}
}
}
}
d.updateChecked()
d.refreshButtonStatus()
}
}
}
FluLoader{
sourceComponent: control.orientation === Qt.Vertical ? com_vertical : com_horizontal
}
} }

View File

@ -10,19 +10,19 @@ QtObject {
windows.push(window) windows.push(window)
} }
} }
function removeWindow(window) { function removeWindow(win) {
if(!window.transientParent){ if(!win.transientParent){
var index = windows.indexOf(window) var index = windows.indexOf(win)
if (index !== -1) { if (index !== -1) {
windows.splice(index, 1) windows.splice(index, 1)
FluTools.deleteLater(window) win.deleteLater()
} }
} }
} }
function exit(retCode){ function exit(retCode){
for(var i =0 ;i< windows.length; i++){ for(var i =0 ;i< windows.length; i++){
var item = windows[i] var win = windows[i]
FluTools.deleteLater(item) win.deleteLater()
} }
windows = [] windows = []
Qt.exit(retCode) Qt.exit(retCode)

View File

@ -171,7 +171,7 @@ T.ScrollBar {
,Transition { ,Transition {
to: "show" to: "show"
SequentialAnimation { SequentialAnimation {
PauseAnimation { duration: 450 } PauseAnimation { duration: 150 }
NumberAnimation { NumberAnimation {
target: rect_bar target: rect_bar
properties: vertical ? "width" : "height" properties: vertical ? "width" : "height"

View File

@ -4,7 +4,7 @@ import FluentUI 1.0
Item { Item {
//DropShadow //DropShadow
property color color: FluTheme.dark ? "#AAAAAA" : "#999999" property color color: FluTheme.dark ? "#000000" : "#999999"
property int elevation: 5 property int elevation: 5
property int radius: 4 property int radius: 4
id:control id:control

View File

@ -10,7 +10,30 @@ FluIconButton {
property string positiveText: qsTr("Save") property string positiveText: qsTr("Save")
property string neutralText: qsTr("Cancel") property string neutralText: qsTr("Cancel")
property string negativeText: qsTr("Reset") property string negativeText: qsTr("Reset")
property bool registered: true
property color errorColor: Qt.rgba(250/255,85/255,85/255,1)
property FluHotkey syncHotkey: undefined
signal accepted() signal accepted()
padding: 0
verticalPadding: 0
horizontalPadding: 0
onSyncHotkeyChanged: {
current = syncHotkey.sequence.split("+")
control.registered = syncHotkey.isRegistered
control.registered = Qt.binding(function(){
return syncHotkey.isRegistered
})
}
text: ""
color: {
if(!enabled){
return disableColor
}
if(pressed){
return pressedColor
}
return hovered ? hoverColor : normalColor
}
QtObject{ QtObject{
id: d id: d
function keyToString(key_code,shift = true) function keyToString(key_code,shift = true)
@ -112,15 +135,80 @@ FluIconButton {
return ""; return "";
} }
} }
background: Rectangle{ background: Item{
border.color: FluTheme.dark ? "#505050" : "#DFDFDF"
border.width: 1
implicitHeight: 42 implicitHeight: 42
implicitWidth: layout_row.width+28 implicitWidth: 42
radius: control.radius }
color:control.color contentItem: Item{
FluFocusRectangle{ implicitWidth: childrenRect.width
visible: control.activeFocus implicitHeight: layout_row.height
FluText{
id: text_title
text: control.text
visible: control.text !== ""
rightPadding: 8
anchors{
verticalCenter: layout_rect.verticalCenter
}
}
Rectangle{
id: layout_rect
border.color: FluTheme.dark ? "#505050" : "#DFDFDF"
border.width: 1
radius: control.radius
color: control.color
height: control.height
width: layout_row.width
anchors{
left: text_title.right
}
FluFocusRectangle{
visible: control.activeFocus
}
Row{
id:layout_row
spacing: 5
anchors.centerIn: parent
Item{
width: 8
height: 1
}
Repeater{
model: control.current
delegate: Loader{
property var keyText: modelData
sourceComponent: com_item_key
}
}
Item{
width: 3
height: 1
}
FluIcon{
iconSource: FluentIcons.EditMirrored
iconSize: 13
anchors{
verticalCenter: parent.verticalCenter
}
}
Item{
width: 8
height: 1
}
}
}
FluText{
id: text_error
text: qsTr("Conflict")
color: control.errorColor
visible: !control.registered
anchors{
verticalCenter: layout_rect.verticalCenter
left: layout_rect.right
leftMargin: 4
}
} }
} }
Component{ Component{
@ -139,29 +227,6 @@ FluIconButton {
} }
} }
} }
Row{
id:layout_row
spacing: 5
anchors.centerIn: parent
Repeater{
model: control.current
delegate: Loader{
property var keyText: modelData
sourceComponent: com_item_key
}
}
Item{
width: 3
height: 1
}
FluIcon{
iconSource: FluentIcons.EditMirrored
iconSize: 13
anchors{
verticalCenter: parent.verticalCenter
}
}
}
FluContentDialog{ FluContentDialog{
id:content_dialog id:content_dialog
property var keysModel: [] property var keysModel: []
@ -179,6 +244,9 @@ FluIconButton {
onPositiveClicked: { onPositiveClicked: {
control.current = content_dialog.keysModel control.current = content_dialog.keysModel
control.accepted() control.accepted()
if(control.syncHotkey){
control.syncHotkey.sequence = control.current.join("+")
}
} }
onNegativeClickListener: function(){ onNegativeClickListener: function(){
content_dialog.keysModel = control.current content_dialog.keysModel = control.current

View File

@ -9,14 +9,41 @@ Rectangle {
readonly property alias rows: table_view.rows readonly property alias rows: table_view.rows
readonly property alias columns: table_view.columns readonly property alias columns: table_view.columns
readonly property alias current: d.current readonly property alias current: d.current
readonly property alias sourceModel: table_model property var sourceModel:FluTableModel {
property var columnSource columnSource: control.columnSource
}
property var columnSource: []
property var dataSource property var dataSource
property color borderColor: FluTheme.dark ? Qt.rgba(37/255,37/255,37/255,1) : Qt.rgba(228/255,228/255,228/255,1) property color borderColor: FluTheme.dark ? Qt.rgba(37/255,37/255,37/255,1) : Qt.rgba(228/255,228/255,228/255,1)
property bool horizonalHeaderVisible: true property bool horizonalHeaderVisible: true
property bool verticalHeaderVisible: true property bool verticalHeaderVisible: true
property color selectedBorderColor: FluTheme.primaryColor property color selectedBorderColor: FluTheme.primaryColor
property color selectedColor: FluTools.withOpacity(FluTheme.primaryColor,0.3) property color selectedColor: FluTools.withOpacity(FluTheme.primaryColor,0.3)
property alias view: table_view
property var columnWidthProvider: function(column) {
var columnModel = control.columnSource[column]
var width = columnModel.width
if(width){
return width
}
var minimumWidth = columnModel.minimumWidth
if(minimumWidth){
return minimumWidth
}
return d.defaultItemWidth
}
property var rowHeightProvider: function(row) {
var rowModel = control.getRow(row)
var height = rowModel.height
if(height){
return height
}
var minimumHeight = rowModel._minimumHeight
if(minimumHeight){
return minimumHeight
}
return d.defaultItemHeight
}
id:control id:control
color: { color: {
if(Window.active){ if(Window.active){
@ -27,67 +54,69 @@ Rectangle {
onColumnSourceChanged: { onColumnSourceChanged: {
if(columnSource.length!==0){ if(columnSource.length!==0){
var columns= [] var columns= []
var columnsData = []
var headerRow = {} var headerRow = {}
columnSource.forEach(function(item){ var offsetX = 0
var column = Qt.createQmlObject('import Qt.labs.qmlmodels 1.0;TableModelColumn{}',table_model); for(var i=0;i<=columnSource.length-1;i++){
var item = columnSource[i]
if(!item.width){
item.width = d.defaultItemWidth
}
item.x = offsetX
offsetX = offsetX + item.width
var column = Qt.createQmlObject('import Qt.labs.qmlmodels 1.0;TableModelColumn{}',sourceModel);
column.display = item.dataIndex column.display = item.dataIndex
columnsData.push(item)
columns.push(column) columns.push(column)
headerRow[item.dataIndex] = item.title headerRow[item.dataIndex] = item
}) }
d.columns_data = columnsData
table_model.columns = columns
header_column_model.columns = columns header_column_model.columns = columns
header_column_model.rows = [headerRow] header_column_model.rows = [headerRow]
} }
} }
Component.onDestruction: {
table_view.contentY = 0
}
QtObject{ QtObject{
id:d id:d
property var current property var current
property int rowHoverIndex: -1 property int rowHoverIndex: -1
property int defaultItemWidth: 100 property int defaultItemWidth: 100
property int defaultItemHeight: 42 property int defaultItemHeight: 42
property var columns_data: []
property var editDelegate property var editDelegate
property var editPosition property var editPosition
signal tableItemLayout(int column)
function getEditDelegate(column){ function getEditDelegate(column){
var obj =d.columns_data[column].editDelegate var obj =control.columnSource[column].editDelegate
if(obj){ if(obj){
return obj return obj
} }
if(d.columns_data[column].editMultiline === true){ if(control.columnSource[column].editMultiline === true){
return com_edit_multiline return com_edit_multiline
} }
return com_edit return com_edit
} }
} }
onDataSourceChanged: { onDataSourceChanged: {
table_model.clear() sourceModel.clear()
table_model.rows = dataSource sourceModel.rows = dataSource
}
TableModel {
id:table_model
TableModelColumn {}
} }
TableModel{ TableModel{
id:header_column_model id: header_column_model
TableModelColumn {} TableModelColumn { display : "title"}
} }
TableModel{ TableModel{
id:header_row_model id: header_row_model
TableModelColumn { display: "rowIndex" } TableModelColumn { display: "rowIndex" }
} }
FluTableSortProxyModel{ FluTableSortProxyModel{
id:table_sort_model id: table_sort_model
model: table_model model: control.sourceModel
} }
Component{ Component{
id:com_edit id:com_edit
FluTextBox{ FluTextBox{
id:text_box id:text_box
text: String(display) text: String(display)
readOnly: true === d.columns_data[column].readOnly readOnly: true === control.columnSource[column].readOnly
Component.onCompleted: { Component.onCompleted: {
forceActiveFocus() forceActiveFocus()
selectAll() selectAll()
@ -113,7 +142,7 @@ Rectangle {
TextArea.flickable: FluMultilineTextBox { TextArea.flickable: FluMultilineTextBox {
id:text_box id:text_box
text: String(display) text: String(display)
readOnly: true === d.columns_data[column].readOnly readOnly: true === control.columnSource[column].readOnly
verticalAlignment: TextInput.AlignVCenter verticalAlignment: TextInput.AlignVCenter
isCtrlEnterForNewline: true isCtrlEnterForNewline: true
Component.onCompleted: { Component.onCompleted: {
@ -196,19 +225,42 @@ Rectangle {
id:com_table_delegate id:com_table_delegate
MouseArea{ MouseArea{
id:item_table_mouse id:item_table_mouse
property var _model: model
property bool isMainTable: TableView.view == table_view
property var currentTableView: TableView.view
property bool isHide: {
if(isMainTable && columnModel.frozen){
return true
}
if(!isMainTable){
if(currentTableView.dataIndex !== columnModel.dataIndex)
return true
}
return false
}
property bool isRowSelected: {
if(!rowModel)
return false
if(d.current){
return rowModel._key === d.current._key
}
return false
}
property bool editVisible: {
if(!rowModel)
return false
if(d.editPosition && d.editPosition._key === rowModel._key && d.editPosition.column === column){
return true
}
return false
}
implicitWidth: isHide ? Number.MIN_VALUE : TableView.view.width
visible: !isHide
TableView.onPooled: { TableView.onPooled: {
if(d.editPosition && d.editPosition.row === row && d.editPosition.column === column){ if(d.editPosition && d.editPosition.row === row && d.editPosition.column === column){
control.closeEditor() control.closeEditor()
} }
} }
property var rowObject : control.getRow(row)
property var itemModel: model
property bool editVisible: {
if(rowObject && d.editPosition && d.editPosition._key === rowObject._key && d.editPosition.column === column){
return true
}
return false
}
hoverEnabled: true hoverEnabled: true
onEntered: { onEntered: {
d.rowHoverIndex = row d.rowHoverIndex = row
@ -217,25 +269,37 @@ Rectangle {
if(editVisible){ if(editVisible){
updateEditPosition() updateEditPosition()
} }
if(isMainTable){
updateTableItem()
}
} }
onHeightChanged: { onHeightChanged: {
if(editVisible){ if(editVisible){
updateEditPosition() updateEditPosition()
} }
if(isMainTable){
updateTableItem()
}
} }
onXChanged: { onXChanged: {
if(editVisible){ if(editVisible){
updateEditPosition() updateEditPosition()
} }
if(isMainTable){
updateTableItem()
}
} }
onYChanged: { onYChanged: {
if(editVisible){ if(editVisible){
updateEditPosition() updateEditPosition()
} }
if(isMainTable){
updateTableItem()
}
} }
function updateEditPosition(){ function updateEditPosition(){
var obj = {} var obj = {}
obj._key = rowObject._key obj._key = rowModel._key
obj.column = column obj.column = column
obj.row = row obj.row = row
obj.x = item_table_mouse.x obj.x = item_table_mouse.x
@ -244,26 +308,22 @@ Rectangle {
obj.height = item_table_mouse.height - 2 obj.height = item_table_mouse.height - 2
d.editPosition = obj d.editPosition = obj
} }
function updateTableItem(){
var columnModel = control.columnSource[column]
columnModel.x = item_table_mouse.x
columnModel.y = item_table_mouse.y
d.tableItemLayout(column)
}
Rectangle{ Rectangle{
id:item_table
anchors.fill: parent anchors.fill: parent
property point position: Qt.point(column,row)
property bool isRowSelected: {
if(rowObject === null)
return false
if(d.current){
return rowObject._key === d.current._key
}
return false
}
color:{ color:{
if(item_table.isRowSelected){ if(item_table_mouse.isRowSelected){
return control.selectedColor return control.selectedColor
} }
if(d.rowHoverIndex === row || item_table.isRowSelected){ if(d.rowHoverIndex === row || item_table_mouse.isRowSelected){
return FluTheme.dark ? Qt.rgba(1,1,1,0.06) : Qt.rgba(0,0,0,0.06) return FluTheme.dark ? Qt.rgba(1,1,1,0.06) : Qt.rgba(0,0,0,0.06)
} }
return (row%2!==0) ? control.color : (FluTheme.dark ? Qt.rgba(1,1,1,0.015) : Qt.rgba(0,0,0,0.015)) return (row%2!==0) ? control.color : (FluTheme.dark ? Qt.rgba(1,1,1,0.03) : Qt.rgba(0,0,0,0.03))
} }
MouseArea{ MouseArea{
anchors.fill: parent anchors.fill: parent
@ -276,25 +336,28 @@ Rectangle {
onReleased: { onReleased: {
} }
onDoubleClicked:{ onDoubleClicked:{
if(typeof(display) == "object"){ if(item_table_loader.isObject){
return return
} }
loader_edit.display = display loader_edit.display = item_table_loader.display
d.editDelegate = d.getEditDelegate(column) d.editDelegate = d.getEditDelegate(column)
updateEditPosition() item_table_mouse.updateEditPosition()
} }
onClicked: onClicked:
(event)=>{ (event)=>{
d.current = rowObject d.current = rowModel
control.closeEditor() control.closeEditor()
event.accepted = true event.accepted = true
} }
} }
FluLoader{ FluLoader{
property var model: itemModel id: item_table_loader
property var display: itemModel.display property var model: item_table_mouse._model
property int row: item_table.position.y property var display: rowModel[columnModel.dataIndex]
property int column: item_table.position.x 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 bool isObject: typeof(display) == "object"
property var options: { property var options: {
if(isObject){ if(isObject){
@ -304,15 +367,53 @@ Rectangle {
} }
anchors.fill: parent anchors.fill: parent
sourceComponent: { sourceComponent: {
if(isObject){ if(item_table_mouse.visible){
return display.comId 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{ Item{
anchors.fill: parent anchors.fill: parent
visible: item_table.isRowSelected visible: item_table_mouse.isRowSelected
Rectangle{ Rectangle{
width: 1 width: 1
height: parent.height height: parent.height
@ -343,6 +444,11 @@ Rectangle {
} }
} }
} }
onWidthChanged:{
table_view.forceLayout()
}
MouseArea{ MouseArea{
id:layout_mouse_table id:layout_mouse_table
hoverEnabled: true hoverEnabled: true
@ -364,30 +470,8 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
ScrollBar.horizontal:scroll_bar_h ScrollBar.horizontal:scroll_bar_h
ScrollBar.vertical:scroll_bar_v ScrollBar.vertical:scroll_bar_v
columnWidthProvider: function(column) { columnWidthProvider: control.columnWidthProvider
var columnObject = d.columns_data[column] rowHeightProvider: control.rowHeightProvider
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
}
model: table_sort_model model: table_sort_model
clip: true clip: true
onRowsChanged: { onRowsChanged: {
@ -395,70 +479,51 @@ Rectangle {
table_view.flick(0,1) table_view.flick(0,1)
} }
delegate: com_table_delegate 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{ Component{
id:com_column_header_delegate id: com_column_header_delegate
Rectangle{ Rectangle{
id:column_item_control id: column_item_control
property var currentTableView : TableView.view
readonly property real cellPadding: 8 readonly property real cellPadding: 8
property bool canceled: false property bool canceled: false
property int columnIndex: column property var _model: model
readonly property var columnObject : d.columns_data[column] readonly property var columnModel : control.columnSource[_index]
implicitWidth: { readonly property int _index : {
return (item_column_loader.item && item_column_loader.item.implicitWidth) + (cellPadding * 2) 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) color: FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(247/255,247/255,247/255,1)
Rectangle{ Rectangle{
border.color: control.borderColor border.color: control.borderColor
@ -479,7 +544,7 @@ Rectangle {
width: 1 width: 1
height: parent.height height: parent.height
anchors.left: parent.left anchors.left: parent.left
visible: column !== 0 visible: column_item_control._index !== 0
color:"#00000000" color:"#00000000"
} }
Rectangle{ Rectangle{
@ -488,7 +553,7 @@ Rectangle {
height: parent.height height: parent.height
anchors.right: parent.right anchors.right: parent.right
color:"#00000000" color:"#00000000"
visible: column === table_view.columns - 1 visible: column_item_control._index === table_view.columns - 1
} }
MouseArea{ MouseArea{
id:column_item_control_mouse id:column_item_control_mouse
@ -510,22 +575,23 @@ Rectangle {
} }
FluLoader{ FluLoader{
id:item_column_loader id:item_column_loader
property var itemModel: model property var model: column_item_control._model
property var modelData: model.display property var display: model.display.title
property var tableView: table_view property var tableView: table_view
property var tableModel: table_model property var sourceModel: control.sourceModel
property bool isObject: typeof(display) == "object"
property var options:{ property var options:{
if(typeof(modelData) == "object"){ if(isObject){
return modelData.options return display.options
} }
return {} return {}
} }
property int column: column_item_control.columnIndex property int column: column_item_control._index
width: parent.width width: parent.width
height: parent.height height: parent.height
sourceComponent: { sourceComponent: {
if(typeof(modelData) == "object"){ if(isObject){
return modelData.comId return display.comId
} }
return com_column_text return com_column_text
} }
@ -537,7 +603,7 @@ Rectangle {
anchors.right: parent.right anchors.right: parent.right
acceptedButtons: Qt.LeftButton acceptedButtons: Qt.LeftButton
hoverEnabled: true 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 cursorShape: Qt.SplitHCursor
preventStealing: true preventStealing: true
onPressed : onPressed :
@ -557,9 +623,9 @@ Rectangle {
return return
} }
var delta = Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y) var delta = Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y)
var minimumWidth = columnObject.minimumWidth var minimumWidth = columnModel.minimumWidth
var maximumWidth = columnObject.maximumWidth var maximumWidth = columnModel.maximumWidth
var w = columnObject.width var w = columnModel.width
if(!w){ if(!w){
w = d.defaultItemWidth w = d.defaultItemWidth
} }
@ -569,7 +635,7 @@ Rectangle {
if(!maximumWidth){ if(!maximumWidth){
maximumWidth = 65535 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() table_view.forceLayout()
header_horizontal.forceLayout() header_horizontal.forceLayout()
} }
@ -582,7 +648,7 @@ Rectangle {
id:item_control id:item_control
readonly property real cellPadding: 8 readonly property real cellPadding: 8
property bool canceled: false 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)) implicitWidth: Math.max(30, row_text.implicitWidth + (cellPadding * 2))
implicitHeight: row_text.implicitHeight + (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) 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 cursorShape: Qt.SplitVCursor
preventStealing: true preventStealing: true
visible: { visible: {
if(rowObject === null) if(rowModel === null)
return false 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 : onPressed :
(mouse)=>{ (mouse)=>{
@ -668,11 +734,11 @@ Rectangle {
if(!pressed){ if(!pressed){
return return
} }
var rowObject = control.getRow(row) var rowModel = control.getRow(row)
var delta = Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y) var delta = Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y)
var minimumHeight = rowObject._minimumHeight var minimumHeight = rowModel._minimumHeight
var maximumHeight = rowObject._maximumHeight var maximumHeight = rowModel._maximumHeight
var h = rowObject.height var h = rowModel.height
if(!h){ if(!h){
h = d.defaultItemHeight h = d.defaultItemHeight
} }
@ -682,8 +748,8 @@ Rectangle {
if(!maximumHeight){ if(!maximumHeight){
maximumHeight = 65535 maximumHeight = 65535
} }
rowObject.height = Math.min(Math.max(minimumHeight, h + delta.y),maximumHeight) rowModel.height = Math.min(Math.max(minimumHeight, h + delta.y),maximumHeight)
control.setRow(row,rowObject) control.setRow(row,rowModel)
table_view.forceLayout() table_view.forceLayout()
} }
} }
@ -693,7 +759,7 @@ Rectangle {
id:com_column_text id:com_column_text
FluText { FluText {
id: column_text id: column_text
text: modelData text: String(display)
anchors.fill: parent anchors.fill: parent
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter 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<control.columnSource.length;i++){
item = control.columnSource[i]
if(item.frozen){
maxX = maxX - table_view.columnWidthProvider(i)
}
}
return Math.min(Math.max(columnModel.x - table_view.contentX,minX),maxX)}
)
}
Component.onCompleted: {
updateLayout()
}
height: control.height
visible: !item_layout_frozen.isHide
sourceComponent: item_layout_frozen.isHide ? undefined : com_table_frozen
}
}
}
FluScrollBar { FluScrollBar {
id: scroll_bar_h id: scroll_bar_h
anchors{ anchors{
@ -842,7 +1040,7 @@ Rectangle {
function sort(callback=undefined){ function sort(callback=undefined){
if(callback){ if(callback){
table_sort_model.setComparator(function(left,right){ table_sort_model.setComparator(function(left,right){
return callback(table_model.getRow(left),table_model.getRow(right)) return callback(sourceModel.getRow(left),sourceModel.getRow(right))
}) })
}else{ }else{
table_sort_model.setComparator(undefined) table_sort_model.setComparator(undefined)
@ -851,7 +1049,7 @@ Rectangle {
function filter(callback=undefined){ function filter(callback=undefined){
if(callback){ if(callback){
table_sort_model.setFilter(function(index){ table_sort_model.setFilter(function(index){
return callback(table_model.getRow(index)) return callback(sourceModel.getRow(index))
}) })
}else{ }else{
table_sort_model.setFilter(undefined) table_sort_model.setFilter(undefined)
@ -873,7 +1071,26 @@ Rectangle {
table_view.model.removeRow(rowIndex,rows) table_view.model.removeRow(rowIndex,rows)
} }
} }
function insertRow(rowIndex,obj){
if(rowIndex>=0 && rowIndex<table_view.rows){
sourceModel.insertRow(rowIndex,obj)
}
}
function currentIndex(){
var index = -1
if(!d.current){
return index
}
for (var i = 0; i <= sourceModel.rowCount-1; i++) {
var sourceItem = sourceModel.getRow(i);
if(sourceItem._key === d.current._key){
index = i
break
}
}
return index
}
function appendRow(obj){ function appendRow(obj){
table_model.appendRow(obj) sourceModel.appendRow(obj)
} }
} }

View File

@ -17,6 +17,7 @@ Rectangle {
property color selectedBorderColor: FluTheme.primaryColor property color selectedBorderColor: FluTheme.primaryColor
property color selectedColor: FluTools.withOpacity(FluTheme.primaryColor,0.3) property color selectedColor: FluTools.withOpacity(FluTheme.primaryColor,0.3)
readonly property alias current: d.current readonly property alias current: d.current
property alias view: table_view
id:control id:control
color: { color: {
if(Window.active){ if(Window.active){
@ -45,6 +46,9 @@ Rectangle {
id:tree_model id:tree_model
columnSource: control.columnSource columnSource: control.columnSource
} }
Component.onDestruction: {
table_view.contentY = 0
}
onDepthPaddingChanged: { onDepthPaddingChanged: {
table_view.forceLayout() table_view.forceLayout()
} }

View File

@ -60,8 +60,9 @@ Window {
signal lazyLoad() signal lazyLoad()
property var _windowRegister property var _windowRegister
property string _route property string _route
id:window property bool _hideShadow: false
color:"transparent" id: window
color: FluTools.isSoftware() ? window.backgroundColor : "transparent"
Component.onCompleted: { Component.onCompleted: {
FluRouter.addWindow(window) FluRouter.addWindow(window)
useSystemAppBar = FluApp.useSystemAppBar useSystemAppBar = FluApp.useSystemAppBar
@ -121,12 +122,21 @@ Window {
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
asynchronous: true asynchronous: true
Component.onCompleted: { Component.onCompleted: {
var geometry = FluTools.desktopAvailableGeometry(window) img_back.updateLayout()
width = geometry.width
height = geometry.height
sourceSize = Qt.size(width,height)
source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath) source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath)
} }
Connections{
target: window
function onScreenChanged(){
img_back.updateLayout()
}
}
function updateLayout(){
var geometry = FluTools.desktopAvailableGeometry(window)
img_back.width = geometry.width
img_back.height = geometry.height
img_back.sourceSize = Qt.size(img_back.width,img_back.height)
}
Connections{ Connections{
target: FluTheme target: FluTheme
function onDesktopImagePathChanged(){ function onDesktopImagePathChanged(){
@ -142,7 +152,7 @@ Window {
} }
Timer{ Timer{
id:timer_update_image id:timer_update_image
interval: 500 interval: 150
onTriggered: { onTriggered: {
img_back.source = "" img_back.source = ""
img_back.source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath) img_back.source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath)
@ -156,7 +166,7 @@ Window {
blurRadius: 64 blurRadius: 64
visible: window.active && FluTheme.blurBehindWindowEnabled visible: window.active && FluTheme.blurBehindWindowEnabled
tintColor: FluTheme.dark ? Qt.rgba(0, 0, 0, 1) : Qt.rgba(1, 1, 1, 1) tintColor: FluTheme.dark ? Qt.rgba(0, 0, 0, 1) : Qt.rgba(1, 1, 1, 1)
targetRect: Qt.rect(window.x,window.y,window.width,window.height) targetRect: Qt.rect(window.x-window.screen.virtualX,window.y-window.screen.virtualY,window.width,window.height)
} }
} }
} }
@ -304,7 +314,6 @@ Window {
return info_bar.clearAllInfo() return info_bar.clearAllInfo()
} }
function moveWindowToDesktopCenter(){ function moveWindowToDesktopCenter(){
screen = Qt.application.screens[FluTools.cursorScreenIndex()]
var availableGeometry = FluTools.desktopAvailableGeometry(window) var availableGeometry = FluTools.desktopAvailableGeometry(window)
window.setGeometry((availableGeometry.width-window.width)/2+Screen.virtualX,(availableGeometry.height-window.height)/2+Screen.virtualY,window.width,window.height) window.setGeometry((availableGeometry.width-window.width)/2+Screen.virtualX,(availableGeometry.height-window.height)/2+Screen.virtualY,window.width,window.height)
} }
@ -344,4 +353,7 @@ Window {
function setHitTestVisible(val){ function setHitTestVisible(val){
frameless.setHitTestVisible(val) frameless.setHitTestVisible(val)
} }
function deleteLater(){
FluTools.deleteLater(window)
}
} }

View File

@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only. // It is used for QML tooling purposes only.
// //
// This file was auto-generated by: // This file was auto-generated by:
// 'qmlplugindump -nonrelocatable FluentUI 1.0 D:\QtProjects\build-FluentUI-Desktop_Qt_5_15_2_MSVC2019_64bit-Release\src' // 'qmlplugindump -nonrelocatable FluentUI 1.0 D:/QtProjects/build-FluentUI-Desktop_Qt_5_15_2_MSVC2019_64bit-Release/src'
Module { Module {
dependencies: ["QtQuick 2.0"] dependencies: ["QtQuick 2.0"]
@ -25,25 +25,28 @@ Module {
name: "FluApp" name: "FluApp"
prototype: "QObject" prototype: "QObject"
exports: ["FluentUI/FluApp 1.0"] exports: ["FluentUI/FluApp 1.0"]
isCreatable: false
isSingleton: true
exportMetaObjectRevisions: [0] exportMetaObjectRevisions: [0]
Property { name: "useSystemAppBar"; type: "bool" } Property { name: "useSystemAppBar"; type: "bool" }
Property { name: "windowIcon"; type: "string" } Property { name: "windowIcon"; type: "string" }
Property { name: "locale"; type: "QLocale" } Property { name: "locale"; type: "QLocale" }
Property { name: "launcher"; type: "QObject"; isPointer: true }
Method { Method {
name: "init" name: "init"
Parameter { name: "target"; type: "QObject"; isPointer: true } Parameter { name: "launcher"; type: "QObject"; isPointer: true }
Parameter { name: "locale"; type: "QLocale" } Parameter { name: "locale"; type: "QLocale" }
} }
Method { Method {
name: "init" name: "init"
Parameter { name: "target"; type: "QObject"; isPointer: true } Parameter { name: "launcher"; type: "QObject"; isPointer: true }
} }
Method { Method {
name: "iconDatas" name: "iconData"
type: "QJsonArray" type: "QJsonArray"
Parameter { name: "keyword"; type: "string" } Parameter { name: "keyword"; type: "string" }
} }
Method { name: "iconDatas"; type: "QJsonArray" } Method { name: "iconData"; type: "QJsonArray" }
} }
Component { Component {
name: "FluCalendarViewType" name: "FluCalendarViewType"
@ -78,6 +81,8 @@ Module {
name: "FluColors" name: "FluColors"
prototype: "QObject" prototype: "QObject"
exports: ["FluentUI/FluColors 1.0"] exports: ["FluentUI/FluColors 1.0"]
isCreatable: false
isSingleton: true
exportMetaObjectRevisions: [0] exportMetaObjectRevisions: [0]
Property { name: "Transparent"; type: "QColor" } Property { name: "Transparent"; type: "QColor" }
Property { name: "Black"; type: "QColor" } Property { name: "Black"; type: "QColor" }
@ -155,6 +160,16 @@ Module {
} }
Method { name: "onDestruction" } Method { name: "onDestruction" }
} }
Component {
name: "FluHotkey"
prototype: "QObject"
exports: ["FluentUI/FluHotkey 1.0"]
exportMetaObjectRevisions: [0]
Property { name: "sequence"; type: "string" }
Property { name: "name"; type: "string" }
Property { name: "isRegistered"; type: "bool"; isReadonly: true }
Signal { name: "activated" }
}
Component { Component {
name: "FluNavigationViewType" name: "FluNavigationViewType"
exports: ["FluentUI/FluNavigationViewType 1.0"] exports: ["FluentUI/FluNavigationViewType 1.0"]
@ -265,11 +280,14 @@ Module {
} }
} }
Component { Component {
name: "FluTableSortProxyModel" name: "FluTableModel"
prototype: "QSortFilterProxyModel" prototype: "QAbstractTableModel"
exports: ["FluentUI/FluTableSortProxyModel 1.0"] exports: ["FluentUI/FluTableModel 1.0"]
exportMetaObjectRevisions: [0] exportMetaObjectRevisions: [0]
Property { name: "model"; type: "QAbstractTableModel"; isPointer: true } Property { name: "columnSource"; type: "QList<QVariantMap>" }
Property { name: "rows"; type: "QList<QVariantMap>" }
Property { name: "rowCount"; type: "int"; isReadonly: true }
Method { name: "clear" }
Method { Method {
name: "getRow" name: "getRow"
type: "QVariant" type: "QVariant"
@ -278,6 +296,46 @@ Module {
Method { Method {
name: "setRow" name: "setRow"
Parameter { name: "rowIndex"; type: "int" } 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" } Parameter { name: "val"; type: "QVariant" }
} }
Method { Method {
@ -298,6 +356,8 @@ Module {
name: "FluTextStyle" name: "FluTextStyle"
prototype: "QObject" prototype: "QObject"
exports: ["FluentUI/FluTextStyle 1.0"] exports: ["FluentUI/FluTextStyle 1.0"]
isCreatable: false
isSingleton: true
exportMetaObjectRevisions: [0] exportMetaObjectRevisions: [0]
Property { name: "family"; type: "string" } Property { name: "family"; type: "string" }
Property { name: "Caption"; type: "QFont" } Property { name: "Caption"; type: "QFont" }
@ -312,6 +372,8 @@ Module {
name: "FluTheme" name: "FluTheme"
prototype: "QObject" prototype: "QObject"
exports: ["FluentUI/FluTheme 1.0"] exports: ["FluentUI/FluTheme 1.0"]
isCreatable: false
isSingleton: true
exportMetaObjectRevisions: [0] exportMetaObjectRevisions: [0]
Property { name: "dark"; type: "bool"; isReadonly: true } Property { name: "dark"; type: "bool"; isReadonly: true }
Property { name: "accentColor"; type: "FluAccentColor"; isPointer: true } Property { name: "accentColor"; type: "FluAccentColor"; isPointer: true }
@ -380,6 +442,8 @@ Module {
name: "FluTools" name: "FluTools"
prototype: "QObject" prototype: "QObject"
exports: ["FluentUI/FluTools 1.0"] exports: ["FluentUI/FluTools 1.0"]
isCreatable: false
isSingleton: true
exportMetaObjectRevisions: [0] exportMetaObjectRevisions: [0]
Method { name: "qtMajor"; type: "int" } Method { name: "qtMajor"; type: "int" }
Method { name: "qtMinor"; type: "int" } Method { name: "qtMinor"; type: "int" }
@ -499,7 +563,7 @@ Module {
} }
Component { Component {
name: "FluTreeModel" name: "FluTreeModel"
prototype: "QAbstractItemModel" prototype: "QAbstractTableModel"
exports: ["FluentUI/FluTreeModel 1.0"] exports: ["FluentUI/FluTreeModel 1.0"]
exportMetaObjectRevisions: [0] exportMetaObjectRevisions: [0]
Property { name: "dataSourceSize"; type: "int" } Property { name: "dataSourceSize"; type: "int" }
@ -2316,6 +2380,7 @@ Module {
Parameter { name: "selection"; type: "QItemSelection" } Parameter { name: "selection"; type: "QItemSelection" }
} }
} }
Component { name: "QAbstractTableModel"; prototype: "QAbstractItemModel" }
Component { Component {
name: "QSortFilterProxyModel" name: "QSortFilterProxyModel"
prototype: "QAbstractProxyModel" prototype: "QAbstractProxyModel"
@ -2442,37 +2507,37 @@ Module {
Property { name: "darkClickListener"; type: "QVariant" } Property { name: "darkClickListener"; type: "QVariant" }
Property { Property {
name: "buttonStayTop" name: "buttonStayTop"
type: "FluIconButton_QMLTYPE_20" type: "FluIconButton_QMLTYPE_18"
isReadonly: true isReadonly: true
isPointer: true isPointer: true
} }
Property { Property {
name: "buttonMinimize" name: "buttonMinimize"
type: "FluIconButton_QMLTYPE_20" type: "FluIconButton_QMLTYPE_18"
isReadonly: true isReadonly: true
isPointer: true isPointer: true
} }
Property { Property {
name: "buttonMaximize" name: "buttonMaximize"
type: "FluIconButton_QMLTYPE_20" type: "FluIconButton_QMLTYPE_18"
isReadonly: true isReadonly: true
isPointer: true isPointer: true
} }
Property { Property {
name: "buttonClose" name: "buttonClose"
type: "FluIconButton_QMLTYPE_20" type: "FluIconButton_QMLTYPE_18"
isReadonly: true isReadonly: true
isPointer: true isPointer: true
} }
Property { Property {
name: "buttonDark" name: "buttonDark"
type: "FluIconButton_QMLTYPE_20" type: "FluIconButton_QMLTYPE_18"
isReadonly: true isReadonly: true
isPointer: true isPointer: true
} }
Property { Property {
name: "layoutMacosButtons" name: "layoutMacosButtons"
type: "FluLoader_QMLTYPE_14" type: "FluLoader_QMLTYPE_16"
isReadonly: true isReadonly: true
isPointer: true isPointer: true
} }
@ -2493,6 +2558,7 @@ Module {
Property { name: "items"; type: "QVariant" } Property { name: "items"; type: "QVariant" }
Property { name: "emptyText"; type: "string" } Property { name: "emptyText"; type: "string" }
Property { name: "autoSuggestBoxReplacement"; type: "int" } Property { name: "autoSuggestBoxReplacement"; type: "int" }
Property { name: "textRole"; type: "string" }
Property { name: "filter"; type: "QVariant" } Property { name: "filter"; type: "QVariant" }
Signal { Signal {
name: "itemClicked" name: "itemClicked"
@ -2695,6 +2761,7 @@ Module {
Property { name: "normalColor"; type: "QColor" } Property { name: "normalColor"; type: "QColor" }
Property { name: "hoverColor"; type: "QColor" } Property { name: "hoverColor"; type: "QColor" }
Property { name: "disableColor"; type: "QColor" } Property { name: "disableColor"; type: "QColor" }
Property { name: "textBox"; type: "QQuickTextField"; isReadonly: true; isPointer: true }
Signal { Signal {
name: "commit" name: "commit"
Parameter { name: "text"; type: "string" } Parameter { name: "text"; type: "string" }
@ -3043,6 +3110,7 @@ Module {
Parameter { name: "itemcomponent"; type: "QVariant" } Parameter { name: "itemcomponent"; type: "QVariant" }
Parameter { name: "duration"; type: "QVariant" } Parameter { name: "duration"; type: "QVariant" }
} }
Method { name: "clearAllInfo"; type: "QVariant" }
Property { name: "children"; type: "QObject"; isList: true; isReadonly: true } Property { name: "children"; type: "QObject"; isList: true; isReadonly: true }
} }
Component { Component {
@ -3162,15 +3230,15 @@ Module {
defaultProperty: "data" defaultProperty: "data"
Property { name: "logo"; type: "QUrl" } Property { name: "logo"; type: "QUrl" }
Property { name: "title"; type: "string" } Property { name: "title"; type: "string" }
Property { name: "items"; type: "FluObject_QMLTYPE_172"; isPointer: true } Property { name: "items"; type: "FluObject_QMLTYPE_164"; isPointer: true }
Property { name: "footerItems"; type: "FluObject_QMLTYPE_172"; isPointer: true } Property { name: "footerItems"; type: "FluObject_QMLTYPE_164"; isPointer: true }
Property { name: "displayMode"; type: "int" } Property { name: "displayMode"; type: "int" }
Property { name: "autoSuggestBox"; type: "QQmlComponent"; isPointer: true } Property { name: "autoSuggestBox"; type: "QQmlComponent"; isPointer: true }
Property { name: "actionItem"; type: "QQmlComponent"; isPointer: true } Property { name: "actionItem"; type: "QQmlComponent"; isPointer: true }
Property { name: "topPadding"; type: "int" } Property { name: "topPadding"; type: "int" }
Property { name: "pageMode"; type: "int" } Property { name: "pageMode"; type: "int" }
Property { name: "navItemRightMenu"; type: "FluMenu_QMLTYPE_33"; isPointer: true } Property { name: "navItemRightMenu"; type: "FluMenu_QMLTYPE_36"; isPointer: true }
Property { name: "navItemExpanderRightMenu"; type: "FluMenu_QMLTYPE_33"; isPointer: true } Property { name: "navItemExpanderRightMenu"; type: "FluMenu_QMLTYPE_36"; isPointer: true }
Property { name: "navCompactWidth"; type: "int" } Property { name: "navCompactWidth"; type: "int" }
Property { name: "navTopMargin"; type: "int" } Property { name: "navTopMargin"; type: "int" }
Property { name: "cellHeight"; type: "int" } Property { name: "cellHeight"; type: "int" }
@ -3178,13 +3246,13 @@ Module {
Property { name: "hideNavAppBar"; type: "bool" } Property { name: "hideNavAppBar"; type: "bool" }
Property { Property {
name: "buttonMenu" name: "buttonMenu"
type: "FluIconButton_QMLTYPE_20" type: "FluIconButton_QMLTYPE_18"
isReadonly: true isReadonly: true
isPointer: true isPointer: true
} }
Property { Property {
name: "buttonBack" name: "buttonBack"
type: "FluIconButton_QMLTYPE_20" type: "FluIconButton_QMLTYPE_18"
isReadonly: true isReadonly: true
isPointer: true isPointer: true
} }
@ -3246,6 +3314,8 @@ Module {
Property { name: "pageCount"; type: "int" } Property { name: "pageCount"; type: "int" }
Property { name: "__itemPerPage"; type: "int" } Property { name: "__itemPerPage"; type: "int" }
Property { name: "__pageButtonHalf"; type: "int" } Property { name: "__pageButtonHalf"; type: "int" }
Property { name: "header"; type: "QQmlComponent"; isPointer: true }
Property { name: "footer"; type: "QQmlComponent"; isPointer: true }
Signal { Signal {
name: "requestPage" name: "requestPage"
Parameter { name: "page"; type: "int" } Parameter { name: "page"; type: "int" }
@ -3470,14 +3540,18 @@ Module {
Property { name: "textColor"; type: "QColor" } Property { name: "textColor"; type: "QColor" }
} }
Component { Component {
prototype: "QQuickColumnLayout" prototype: "QQuickItem"
name: "FluentUI/FluRadioButtons 1.0" name: "FluentUI/FluRadioButtons 1.0"
exports: ["FluentUI/FluRadioButtons 1.0"] exports: ["FluentUI/FluRadioButtons 1.0"]
exportMetaObjectRevisions: [0] exportMetaObjectRevisions: [0]
isComposite: true isComposite: true
defaultProperty: "buttons" defaultProperty: "buttons"
Property { name: "currentIndex"; type: "int" }
Property { name: "buttons"; type: "QObject"; isList: true; isReadonly: true } 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 { Component {
prototype: "QQuickRangeSlider" prototype: "QQuickRangeSlider"
@ -3632,6 +3706,9 @@ Module {
Property { name: "positiveText"; type: "string" } Property { name: "positiveText"; type: "string" }
Property { name: "neutralText"; type: "string" } Property { name: "neutralText"; type: "string" }
Property { name: "negativeText"; 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" } Signal { name: "accepted" }
Property { name: "iconSize"; type: "int" } Property { name: "iconSize"; type: "int" }
Property { name: "iconSource"; type: "int" } Property { name: "iconSource"; type: "int" }
@ -3757,6 +3834,7 @@ Module {
exportMetaObjectRevisions: [0] exportMetaObjectRevisions: [0]
isComposite: true isComposite: true
defaultProperty: "data" defaultProperty: "data"
Property { name: "sourceModel"; type: "QVariant" }
Property { name: "columnSource"; type: "QVariant" } Property { name: "columnSource"; type: "QVariant" }
Property { name: "dataSource"; type: "QVariant" } Property { name: "dataSource"; type: "QVariant" }
Property { name: "borderColor"; type: "QColor" } Property { name: "borderColor"; type: "QColor" }
@ -3764,10 +3842,12 @@ Module {
Property { name: "verticalHeaderVisible"; type: "bool" } Property { name: "verticalHeaderVisible"; type: "bool" }
Property { name: "selectedBorderColor"; type: "QColor" } Property { name: "selectedBorderColor"; type: "QColor" }
Property { name: "selectedColor"; 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: "rows"; type: "int"; isReadonly: true }
Property { name: "columns"; type: "int"; isReadonly: true } Property { name: "columns"; type: "int"; isReadonly: true }
Property { name: "current"; type: "QVariant"; 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: "closeEditor"; type: "QVariant" }
Method { name: "resetPosition"; type: "QVariant" } Method { name: "resetPosition"; type: "QVariant" }
Method { Method {
@ -3803,6 +3883,13 @@ Module {
Parameter { name: "rowIndex"; type: "QVariant" } Parameter { name: "rowIndex"; type: "QVariant" }
Parameter { name: "rows"; 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 { Method {
name: "appendRow" name: "appendRow"
type: "QVariant" type: "QVariant"
@ -4013,6 +4100,7 @@ Module {
Property { name: "selectedBorderColor"; type: "QColor" } Property { name: "selectedBorderColor"; type: "QColor" }
Property { name: "selectedColor"; type: "QColor" } Property { name: "selectedColor"; type: "QColor" }
Property { name: "current"; type: "QVariant"; isReadonly: true } Property { name: "current"; type: "QVariant"; isReadonly: true }
Property { name: "view"; type: "QQuickTableView"; isReadonly: true; isPointer: true }
Method { name: "count"; type: "QVariant" } Method { name: "count"; type: "QVariant" }
Method { name: "visibleCount"; type: "QVariant" } Method { name: "visibleCount"; type: "QVariant" }
Method { Method {
@ -4068,6 +4156,7 @@ Module {
Property { name: "closeListener"; type: "QVariant" } Property { name: "closeListener"; type: "QVariant" }
Property { name: "_windowRegister"; type: "QVariant" } Property { name: "_windowRegister"; type: "QVariant" }
Property { name: "_route"; type: "string" } Property { name: "_route"; type: "string" }
Property { name: "_hideShadow"; type: "bool" }
Property { name: "contentData"; type: "QObject"; isList: true; isReadonly: true } Property { name: "contentData"; type: "QObject"; isList: true; isReadonly: true }
Signal { Signal {
name: "initArgument" name: "initArgument"
@ -4103,6 +4192,7 @@ Module {
Parameter { name: "duration"; type: "QVariant" } Parameter { name: "duration"; type: "QVariant" }
Parameter { name: "moremsg"; type: "QVariant" } Parameter { name: "moremsg"; type: "QVariant" }
} }
Method { name: "clearAllInfo"; type: "QVariant" }
Method { name: "moveWindowToDesktopCenter"; type: "QVariant" } Method { name: "moveWindowToDesktopCenter"; type: "QVariant" }
Method { name: "fixWindowSize"; type: "QVariant" } Method { name: "fixWindowSize"; type: "QVariant" }
Method { Method {
@ -4116,6 +4206,8 @@ Module {
Parameter { name: "data"; type: "QVariant" } Parameter { name: "data"; type: "QVariant" }
} }
Method { name: "showMaximized"; type: "QVariant" } Method { name: "showMaximized"; type: "QVariant" }
Method { name: "showMinimized"; type: "QVariant" }
Method { name: "showNormal"; type: "QVariant" }
Method { Method {
name: "showLoading" name: "showLoading"
type: "QVariant" type: "QVariant"
@ -4136,7 +4228,12 @@ Module {
isComposite: true isComposite: true
defaultProperty: "contentData" defaultProperty: "contentData"
Property { name: "contentDelegate"; type: "QQmlComponent"; isPointer: true } 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: "windowIcon"; type: "string" }
Property { name: "launchMode"; type: "int" } Property { name: "launchMode"; type: "int" }
Property { name: "argument"; type: "QVariant" } Property { name: "argument"; type: "QVariant" }
@ -4162,6 +4259,7 @@ Module {
Property { name: "closeListener"; type: "QVariant" } Property { name: "closeListener"; type: "QVariant" }
Property { name: "_windowRegister"; type: "QVariant" } Property { name: "_windowRegister"; type: "QVariant" }
Property { name: "_route"; type: "string" } Property { name: "_route"; type: "string" }
Property { name: "_hideShadow"; type: "bool" }
Property { name: "contentData"; type: "QObject"; isList: true; isReadonly: true } Property { name: "contentData"; type: "QObject"; isList: true; isReadonly: true }
Signal { Signal {
name: "initArgument" name: "initArgument"
@ -4197,6 +4295,7 @@ Module {
Parameter { name: "duration"; type: "QVariant" } Parameter { name: "duration"; type: "QVariant" }
Parameter { name: "moremsg"; type: "QVariant" } Parameter { name: "moremsg"; type: "QVariant" }
} }
Method { name: "clearAllInfo"; type: "QVariant" }
Method { name: "moveWindowToDesktopCenter"; type: "QVariant" } Method { name: "moveWindowToDesktopCenter"; type: "QVariant" }
Method { name: "fixWindowSize"; type: "QVariant" } Method { name: "fixWindowSize"; type: "QVariant" }
Method { Method {
@ -4210,6 +4309,8 @@ Module {
Parameter { name: "data"; type: "QVariant" } Parameter { name: "data"; type: "QVariant" }
} }
Method { name: "showMaximized"; type: "QVariant" } Method { name: "showMaximized"; type: "QVariant" }
Method { name: "showMinimized"; type: "QVariant" }
Method { name: "showNormal"; type: "QVariant" }
Method { Method {
name: "showLoading" name: "showLoading"
type: "QVariant" type: "QVariant"

View File

@ -1,5 +1,6 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Window import QtQuick.Window
import FluentUI import FluentUI

View File

@ -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 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 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 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, implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding) implicitContentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
@ -39,6 +40,7 @@ T.ComboBox {
opacity: enabled ? 1 : 0.3 opacity: enabled ? 1 : 0.3
} }
contentItem: T.TextField { contentItem: T.TextField {
id: text_field
property bool disabled: !control.editable property bool disabled: !control.editable
leftPadding: !control.mirrored ? 10 : control.editable && activeFocus ? 3 : 1 leftPadding: !control.mirrored ? 10 : control.editable && activeFocus ? 3 : 1
rightPadding: control.mirrored ? 10 : control.editable && activeFocus ? 3 : 1 rightPadding: control.mirrored ? 10 : control.editable && activeFocus ? 3 : 1

View File

@ -1,47 +1,42 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import FluentUI 1.0 import FluentUI
FluObject { FluObject {
property var root; property var root
property int layoutY: 75 property int layoutY: 75
id:control id:control
FluObject{ FluObject{
id:mcontrol id:mcontrol
property string const_success: "success"; property string const_success: "success"
property string const_info: "info"; property string const_info: "info"
property string const_warning: "warning"; property string const_warning: "warning"
property string const_error: "error"; property string const_error: "error"
property int maxWidth: 300; property int maxWidth: 300
property var screenLayout: null; property var screenLayout: null
function create(type,text,duration,moremsg){ function create(type,text,duration,moremsg){
if(screenLayout){ if(screenLayout){
var last = screenLayout.getLastloader(); var last = screenLayout.getLastloader()
if(last.type === type && last.text === text && moremsg === last.moremsg){ if(last.type === type && last.text === text && moremsg === last.moremsg){
last.duration = duration last.duration = duration
if (duration > 0) last.restart(); if (duration > 0) last.restart()
return last; return last
} }
} }
initScreenLayout(); initScreenLayout()
return contentComponent.createObject(screenLayout,{ return contentComponent.createObject(screenLayout,{type:type,text:text,duration:duration,moremsg:moremsg,})
type:type,
text:text,
duration:duration,
moremsg:moremsg,
});
} }
function createCustom(itemcomponent,duration){ function createCustom(itemcomponent,duration){
initScreenLayout(); initScreenLayout()
if(itemcomponent){ if(itemcomponent){
return contentComponent.createObject(screenLayout,{itemcomponent:itemcomponent,duration:duration}); return contentComponent.createObject(screenLayout,{itemcomponent:itemcomponent,duration:duration})
} }
} }
function initScreenLayout(){ function initScreenLayout(){
if(screenLayout == null){ if(screenLayout == null){
screenLayout = screenlayoutComponent.createObject(root); screenLayout = screenlayoutComponent.createObject(root)
screenLayout.y = control.layoutY; screenLayout.y = control.layoutY
screenLayout.z = 100000; screenLayout.z = 100000
} }
} }
Component{ Component{
@ -58,44 +53,44 @@ FluObject {
duration: FluTheme.animationEnabled ? 333 : 0 duration: FluTheme.animationEnabled ? 333 : 0
} }
} }
onChildrenChanged: if(children.length === 0) destroy(); onChildrenChanged: if(children.length === 0) destroy()
function getLastloader(){ function getLastloader(){
if(children.length > 0){ if(children.length > 0){
return children[children.length - 1]; return children[children.length - 1]
} }
return null; return null
} }
} }
} }
Component{ Component{
id:contentComponent id:contentComponent
Item{ Item{
id:content; id:content
property int duration: 1500 property int duration: 1500
property var itemcomponent property var itemcomponent
property string type property string type
property string text property string text
property string moremsg property string moremsg
width: parent.width; width: parent.width
height: loader.height; height: loader.height
function close(){ function close(){
content.destroy(); content.destroy()
} }
function restart(){ function restart(){
delayTimer.restart(); delayTimer.restart()
} }
Timer { Timer {
id:delayTimer id:delayTimer
interval: duration; interval: duration
running: duration > 0; running: duration > 0
repeat: duration > 0 repeat: duration > 0
onTriggered: content.close(); onTriggered: content.close()
} }
FluLoader{ FluLoader{
id:loader; id:loader
x:(parent.width - width) / 2; x:(parent.width - width) / 2
property var _super: content; property var _super: content
scale: item ? 1 : 0; scale: item ? 1 : 0
asynchronous: true asynchronous: true
Behavior on scale { Behavior on scale {
enabled: FluTheme.animationEnabled enabled: FluTheme.animationEnabled
@ -104,30 +99,30 @@ FluObject {
duration: 167 duration: 167
} }
} }
sourceComponent:itemcomponent ? itemcomponent : mcontrol.fluent_sytle; sourceComponent:itemcomponent ? itemcomponent : mcontrol.fluent_sytle
} }
} }
} }
property Component fluent_sytle: Rectangle{ property Component fluent_sytle: Rectangle{
width: rowlayout.width + (btn_close.visible ? 30 : 48); width: rowlayout.width + (btn_close.visible ? 30 : 48)
height: rowlayout.height + 20; height: rowlayout.height + 20
color: { color: {
if(FluTheme.dark){ if(FluTheme.dark){
switch(_super.type){ switch(_super.type){
case mcontrol.const_success: return Qt.rgba(57/255,61/255,27/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_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_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_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{ }else{
switch(_super.type){ switch(_super.type){
case mcontrol.const_success: return "#dff6dd"; case mcontrol.const_success: return Qt.rgba(223/255,246/255,221/255,1)
case mcontrol.const_warning: return "#fff4ce"; case mcontrol.const_warning: return Qt.rgba(255/255,244/255,206/255,1)
case mcontrol.const_info: return "#f4f4f4"; case mcontrol.const_info: return Qt.rgba(244/255,244/255,244/255,1)
case mcontrol.const_error: return "#fde7e9"; case mcontrol.const_error: return Qt.rgba(253/255,231/255,233/255,1)
} }
return "#FFFFFF" return Qt.rgba(1,1,1,1)
} }
} }
FluShadow{ FluShadow{
@ -138,34 +133,34 @@ FluObject {
border.color: { border.color: {
if(FluTheme.dark){ if(FluTheme.dark){
switch(_super.type){ switch(_super.type){
case mcontrol.const_success: return Qt.rgba(56/255,61/255,27/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_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_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_error: return Qt.rgba(67/255,39/255,38/255,1)
} }
return "#FFFFFF" return Qt.rgba(1,1,1,1)
}else{ }else{
switch(_super.type){ switch(_super.type){
case mcontrol.const_success: return "#d2e8d0"; case mcontrol.const_success: return Qt.rgba(210/255,232/255,208/255,1)
case mcontrol.const_warning: return "#f0e6c2"; case mcontrol.const_warning: return Qt.rgba(240/255,230/255,194/255,1)
case mcontrol.const_info: return "#e6e6e6"; case mcontrol.const_info: return Qt.rgba(230/255,230/255,230/255,1)
case mcontrol.const_error: return "#eed9db"; case mcontrol.const_error: return Qt.rgba(238/255,217/255,219/255,1)
} }
return "#FFFFFF" return Qt.rgba(1,1,1,1)
} }
} }
Row{ Row{
id:rowlayout id:rowlayout
x:20; x:20
y:(parent.height - height) / 2; y:(parent.height - height) / 2
spacing: 10 spacing: 10
FluIcon{ FluIcon{
iconSource:{ iconSource:{
switch(_super.type){ switch(_super.type){
case mcontrol.const_success: return FluentIcons.CompletedSolid; case mcontrol.const_success: return FluentIcons.CompletedSolid
case mcontrol.const_warning: return FluentIcons.InfoSolid; case mcontrol.const_warning: return FluentIcons.InfoSolid
case mcontrol.const_info: return FluentIcons.InfoSolid; case mcontrol.const_info: return FluentIcons.InfoSolid
case mcontrol.const_error: return FluentIcons.StatusErrorFull; case mcontrol.const_error: return FluentIcons.StatusErrorFull
}FluentIcons.StatusErrorFull }FluentIcons.StatusErrorFull
return FluentIcons.FA_info_circle return FluentIcons.FA_info_circle
} }
@ -173,20 +168,20 @@ FluObject {
iconColor: { iconColor: {
if(FluTheme.dark){ if(FluTheme.dark){
switch(_super.type){ switch(_super.type){
case mcontrol.const_success: return Qt.rgba(108/255,203/255,95/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_warning: return Qt.rgba(252/255,225/255,0/255,1)
case mcontrol.const_info: return FluTheme.primaryColor; case mcontrol.const_info: return FluTheme.primaryColor
case mcontrol.const_error: return Qt.rgba(255/255,153/255,164/255,1); case mcontrol.const_error: return Qt.rgba(255/255,153/255,164/255,1)
} }
return "#FFFFFF" return Qt.rgba(1,1,1,1)
}else{ }else{
switch(_super.type){ switch(_super.type){
case mcontrol.const_success: return "#0f7b0f"; case mcontrol.const_success: return Qt.rgba(15/255,123/255,15/255,1)
case mcontrol.const_warning: return "#9d5d00"; case mcontrol.const_warning: return Qt.rgba(157/255,93/255,0/255,1)
case mcontrol.const_info: return "#0066b4"; case mcontrol.const_info: return Qt.rgba(0/255,102/255,180/255,1)
case mcontrol.const_error: return "#c42b1c"; 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 id:btn_close
iconSource: FluentIcons.ChromeClose iconSource: FluentIcons.ChromeClose
iconSize: 10 iconSize: 10
y:5 verticalPadding: 0
horizontalPadding: 0
width: 30
height: 20
visible: _super.duration<=0 visible: _super.duration<=0
iconColor: { anchors.verticalCenter: parent.verticalCenter
if(FluTheme.dark){ iconColor: FluTheme.dark ? Qt.rgba(222/255,222/255,222/255,1) : Qt.rgba(97/255,97/255,97/255,1)
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"
}
}
onClicked: _super.close() onClicked: _super.close()
} }
} }
} }
} }
function showSuccess(text,duration=1000,moremsg){ 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){ 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){ 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){ 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){ function showCustom(itemcomponent,duration=1000){
return mcontrol.createCustom(itemcomponent,duration); return mcontrol.createCustom(itemcomponent,duration)
} }
function clearAllInfo(){ function clearAllInfo(){
if(mcontrol.screenLayout != null) { if(mcontrol.screenLayout != null) {

View File

@ -39,7 +39,7 @@ T.Menu {
: false : false
clip: true clip: true
currentIndex: control.currentIndex currentIndex: control.currentIndex
ScrollIndicator.vertical: ScrollIndicator {} ScrollBar.vertical: FluScrollBar{}
} }
background: Rectangle { background: Rectangle {
implicitWidth: 150 implicitWidth: 150

View File

@ -1121,7 +1121,8 @@ Item {
} }
padding: 0 padding: 0
focus: true focus: true
contentItem: Item{ contentItem: FluClip{
radius: [5,5,5,5]
ListView{ ListView{
id:list_view id:list_view
anchors.fill: parent anchors.fill: parent
@ -1146,7 +1147,6 @@ Item {
visible: item_button.activeFocus visible: item_button.activeFocus
radius:4 radius:4
} }
FluLoader{ FluLoader{
id:item_dot_loader id:item_dot_loader
anchors{ anchors{
@ -1161,7 +1161,6 @@ Item {
return undefined return undefined
} }
} }
} }
contentItem: FluText{ contentItem: FluText{
text:modelData.title text:modelData.title
@ -1188,13 +1187,13 @@ Item {
} }
} }
} }
background: FluRectangle{ background: Rectangle{
implicitWidth: 180 implicitWidth: 180
radius: [4,4,4,4] color:FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(252/255,252/255,252/255,1)
FluShadow{ border.color: FluTheme.dark ? Qt.rgba(26/255,26/255,26/255,1) : Qt.rgba(191/255,191/255,191/255,1)
radius: 4 border.width: 1
} radius: 5
color: FluTheme.dark ? Qt.rgba(51/255,48/255,48/255,1) : Qt.rgba(248/255,250/255,253/255,1) FluShadow{}
} }
function showPopup(pos,height,model){ function showPopup(pos,height,model){
background.implicitHeight = height background.implicitHeight = height

View File

@ -1,7 +1,7 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.15 import QtQuick.Layouts
import FluentUI 1.0 import FluentUI
Item { Item {
signal requestPage(int page, int count) signal requestPage(int page, int count)
@ -37,7 +37,6 @@ Item {
} }
Row { Row {
spacing: 5 spacing: 5
FluToggleButton { FluToggleButton {
property int pageNumber: 1 property int pageNumber: 1
visible: control.pageCount > 0 visible: control.pageCount > 0
@ -97,7 +96,6 @@ Item {
sourceComponent: footer sourceComponent: footer
} }
} }
function calcNewPage(page) { function calcNewPage(page) {
if (!page) if (!page)
return return
@ -107,5 +105,4 @@ Item {
control.pageCurrent = page_num control.pageCurrent = page_num
control.requestPage(page_num, control.__itemPerPage) control.requestPage(page_num, control.__itemPerPage)
} }
} }

View File

@ -4,44 +4,87 @@ import QtQuick.Controls.Basic
import QtQuick.Layouts import QtQuick.Layouts
import FluentUI import FluentUI
ColumnLayout { Item{
default property alias buttons: control.data id:control
default property list<QtObject> buttons
property int currentIndex : -1 property int currentIndex : -1
property int spacing: 8
property int orientation: Qt.Vertical
property bool disabled: false property bool disabled: false
property bool manuallyDisabled: false property bool manuallyDisabled: false
id:control QtObject{
onCurrentIndexChanged: { id: d
for(var i = 0;i<buttons.length;i++){ function updateChecked(){
buttons[i].checked = false if(buttons.length === 0){
} return
var button = buttons[currentIndex] }
if(button){ for(var i = 0;i<buttons.length;i++){
button.checked = true buttons[i].checked = false
} }
} if(currentIndex>=0 && currentIndex<buttons.length){
onDisabledChanged: { buttons[currentIndex].checked = true
refreshButtonStatus()
}
onManuallyDisabledChanged: {
refreshButtonStatus()
}
Component.onCompleted: {
for(var i = 0;i<buttons.length;i++){
buttons[i].clickListener = function(){
for(var i = 0;i<buttons.length;i++){
var button = buttons[i]
if(this === button){
currentIndex = i
}
}
} }
} }
refreshButtonStatus() function refreshButtonStatus() {
} for(var i = 0;i<buttons.length;i++){
if(!manuallyDisabled) buttons[i].enabled = !disabled
function refreshButtonStatus() { }
for(var i = 0;i<buttons.length;i++){
if(!manuallyDisabled) buttons[i].enabled = !disabled
} }
} }
implicitWidth: childrenRect.width
implicitHeight: childrenRect.height
onCurrentIndexChanged: {
d.updateChecked()
}
onDisabledChanged: {
d.refreshButtonStatus()
}
onManuallyDisabledChanged: {
d.refreshButtonStatus()
}
Component{
id:com_vertical
ColumnLayout {
data: control.buttons
spacing: control.spacing
Component.onCompleted: {
for(var i = 0;i<control.buttons.length;i++){
control.buttons[i].clickListener = function(){
for(var i = 0;i<control.buttons.length;i++){
var button = control.buttons[i]
if(this === button){
control.currentIndex = i
}
}
}
}
d.updateChecked()
d.refreshButtonStatus()
}
}
}
Component{
id:com_horizontal
RowLayout {
data: control.buttons
spacing: control.spacing
Component.onCompleted: {
for(var i = 0;i<control.buttons.length;i++){
control.buttons[i].clickListener = function(){
for(var i = 0;i<control.buttons.length;i++){
var button = control.buttons[i]
if(this === button){
control.currentIndex = i
}
}
}
}
d.updateChecked()
d.refreshButtonStatus()
}
}
}
FluLoader{
sourceComponent: control.orientation === Qt.Vertical ? com_vertical : com_horizontal
}
} }

View File

@ -11,19 +11,19 @@ QtObject {
windows.push(window) windows.push(window)
} }
} }
function removeWindow(window) { function removeWindow(win) {
if(!window.transientParent){ if(!win.transientParent){
var index = windows.indexOf(window) var index = windows.indexOf(win)
if (index !== -1) { if (index !== -1) {
windows.splice(index, 1) windows.splice(index, 1)
FluTools.deleteLater(window) win.deleteLater()
} }
} }
} }
function exit(retCode){ function exit(retCode){
for(var i =0 ;i< windows.length; i++){ for(var i =0 ;i< windows.length; i++){
var item = windows[i] var win = windows[i]
FluTools.deleteLater(item) win.deleteLater()
} }
windows = [] windows = []
Qt.exit(retCode) Qt.exit(retCode)
@ -56,9 +56,7 @@ QtObject {
var launchMode = win.launchMode var launchMode = win.launchMode
if(launchMode === 1){ if(launchMode === 1){
win.argument = argument win.argument = argument
if(!win.visible){ win.show()
win.visible = true
}
win.raise() win.raise()
win.requestActivate() win.requestActivate()
return return

View File

@ -173,7 +173,7 @@ T.ScrollBar {
,Transition { ,Transition {
to: "show" to: "show"
SequentialAnimation { SequentialAnimation {
PauseAnimation { duration: 450 } PauseAnimation { duration: 150 }
NumberAnimation { NumberAnimation {
target: rect_bar target: rect_bar
properties: vertical ? "width" : "height" properties: vertical ? "width" : "height"

View File

@ -4,7 +4,7 @@ import FluentUI
Item { Item {
//DropShadow //DropShadow
property color color: FluTheme.dark ? "#AAAAAA" : "#999999" property color color: FluTheme.dark ? "#000000" : "#999999"
property int elevation: 5 property int elevation: 5
property int radius: 4 property int radius: 4
id:control id:control

View File

@ -10,7 +10,30 @@ FluIconButton {
property string positiveText: qsTr("Save") property string positiveText: qsTr("Save")
property string neutralText: qsTr("Cancel") property string neutralText: qsTr("Cancel")
property string negativeText: qsTr("Reset") property string negativeText: qsTr("Reset")
property bool registered: true
property color errorColor: Qt.rgba(250/255,85/255,85/255,1)
property FluHotkey syncHotkey: undefined
signal accepted() signal accepted()
padding: 0
verticalPadding: 0
horizontalPadding: 0
onSyncHotkeyChanged: {
current = syncHotkey.sequence.split("+")
control.registered = syncHotkey.isRegistered
control.registered = Qt.binding(function(){
return syncHotkey.isRegistered
})
}
text: ""
color: {
if(!enabled){
return disableColor
}
if(pressed){
return pressedColor
}
return hovered ? hoverColor : normalColor
}
QtObject{ QtObject{
id: d id: d
function keyToString(key_code,shift = true) function keyToString(key_code,shift = true)
@ -112,15 +135,80 @@ FluIconButton {
return ""; return "";
} }
} }
background: Rectangle{ background: Item{
border.color: FluTheme.dark ? "#505050" : "#DFDFDF"
border.width: 1
implicitHeight: 42 implicitHeight: 42
implicitWidth: layout_row.width+28 implicitWidth: 42
radius: control.radius }
color:control.color contentItem: Item{
FluFocusRectangle{ implicitWidth: childrenRect.width
visible: control.activeFocus implicitHeight: layout_row.height
FluText{
id: text_title
text: control.text
visible: control.text !== ""
rightPadding: 8
anchors{
verticalCenter: layout_rect.verticalCenter
}
}
Rectangle{
id: layout_rect
border.color: FluTheme.dark ? "#505050" : "#DFDFDF"
border.width: 1
radius: control.radius
color: control.color
height: control.height
width: layout_row.width
anchors{
left: text_title.right
}
FluFocusRectangle{
visible: control.activeFocus
}
Row{
id:layout_row
spacing: 5
anchors.centerIn: parent
Item{
width: 8
height: 1
}
Repeater{
model: control.current
delegate: Loader{
property var keyText: modelData
sourceComponent: com_item_key
}
}
Item{
width: 3
height: 1
}
FluIcon{
iconSource: FluentIcons.EditMirrored
iconSize: 13
anchors{
verticalCenter: parent.verticalCenter
}
}
Item{
width: 8
height: 1
}
}
}
FluText{
id: text_error
text: qsTr("Conflict")
color: control.errorColor
visible: !control.registered
anchors{
verticalCenter: layout_rect.verticalCenter
left: layout_rect.right
leftMargin: 4
}
} }
} }
Component{ Component{
@ -139,29 +227,6 @@ FluIconButton {
} }
} }
} }
Row{
id:layout_row
spacing: 5
anchors.centerIn: parent
Repeater{
model: control.current
delegate: Loader{
property var keyText: modelData
sourceComponent: com_item_key
}
}
Item{
width: 3
height: 1
}
FluIcon{
iconSource: FluentIcons.EditMirrored
iconSize: 13
anchors{
verticalCenter: parent.verticalCenter
}
}
}
FluContentDialog{ FluContentDialog{
id:content_dialog id:content_dialog
property var keysModel: [] property var keysModel: []
@ -179,6 +244,9 @@ FluIconButton {
onPositiveClicked: { onPositiveClicked: {
control.current = content_dialog.keysModel control.current = content_dialog.keysModel
control.accepted() control.accepted()
if(control.syncHotkey){
control.syncHotkey.sequence = control.current.join("+")
}
} }
onNegativeClickListener: function(){ onNegativeClickListener: function(){
content_dialog.keysModel = control.current content_dialog.keysModel = control.current

View File

@ -9,14 +9,41 @@ Rectangle {
readonly property alias rows: table_view.rows readonly property alias rows: table_view.rows
readonly property alias columns: table_view.columns readonly property alias columns: table_view.columns
readonly property alias current: d.current readonly property alias current: d.current
readonly property alias sourceModel: table_model property var sourceModel:FluTableModel {
property var columnSource columnSource: control.columnSource
}
property var columnSource: []
property var dataSource property var dataSource
property color borderColor: FluTheme.dark ? Qt.rgba(37/255,37/255,37/255,1) : Qt.rgba(228/255,228/255,228/255,1) property color borderColor: FluTheme.dark ? Qt.rgba(37/255,37/255,37/255,1) : Qt.rgba(228/255,228/255,228/255,1)
property bool horizonalHeaderVisible: true property bool horizonalHeaderVisible: true
property bool verticalHeaderVisible: true property bool verticalHeaderVisible: true
property color selectedBorderColor: FluTheme.primaryColor property color selectedBorderColor: FluTheme.primaryColor
property color selectedColor: FluTools.withOpacity(FluTheme.primaryColor,0.3) property color selectedColor: FluTools.withOpacity(FluTheme.primaryColor,0.3)
property alias view: table_view
property var columnWidthProvider: function(column) {
var columnModel = control.columnSource[column]
var width = columnModel.width
if(width){
return width
}
var minimumWidth = columnModel.minimumWidth
if(minimumWidth){
return minimumWidth
}
return d.defaultItemWidth
}
property var rowHeightProvider: function(row) {
var rowModel = control.getRow(row)
var height = rowModel.height
if(height){
return height
}
var minimumHeight = rowModel._minimumHeight
if(minimumHeight){
return minimumHeight
}
return d.defaultItemHeight
}
id:control id:control
color: { color: {
if(Window.active){ if(Window.active){
@ -27,67 +54,69 @@ Rectangle {
onColumnSourceChanged: { onColumnSourceChanged: {
if(columnSource.length!==0){ if(columnSource.length!==0){
var columns= [] var columns= []
var columnsData = []
var headerRow = {} var headerRow = {}
columnSource.forEach(function(item){ var offsetX = 0
var column = Qt.createQmlObject('import Qt.labs.qmlmodels 1.0;TableModelColumn{}',table_model); for(var i=0;i<=columnSource.length-1;i++){
var item = columnSource[i]
if(!item.width){
item.width = d.defaultItemWidth
}
item.x = offsetX
offsetX = offsetX + item.width
var column = Qt.createQmlObject('import Qt.labs.qmlmodels 1.0;TableModelColumn{}',sourceModel);
column.display = item.dataIndex column.display = item.dataIndex
columnsData.push(item)
columns.push(column) columns.push(column)
headerRow[item.dataIndex] = item.title headerRow[item.dataIndex] = item
}) }
d.columns_data = columnsData
table_model.columns = columns
header_column_model.columns = columns header_column_model.columns = columns
header_column_model.rows = [headerRow] header_column_model.rows = [headerRow]
} }
} }
Component.onDestruction: {
table_view.contentY = 0
}
QtObject{ QtObject{
id:d id:d
property var current property var current
property int rowHoverIndex: -1 property int rowHoverIndex: -1
property int defaultItemWidth: 100 property int defaultItemWidth: 100
property int defaultItemHeight: 42 property int defaultItemHeight: 42
property var columns_data: []
property var editDelegate property var editDelegate
property var editPosition property var editPosition
signal tableItemLayout(int column)
function getEditDelegate(column){ function getEditDelegate(column){
var obj =d.columns_data[column].editDelegate var obj =control.columnSource[column].editDelegate
if(obj){ if(obj){
return obj return obj
} }
if(d.columns_data[column].editMultiline === true){ if(control.columnSource[column].editMultiline === true){
return com_edit_multiline return com_edit_multiline
} }
return com_edit return com_edit
} }
} }
onDataSourceChanged: { onDataSourceChanged: {
table_model.clear() sourceModel.clear()
table_model.rows = dataSource sourceModel.rows = dataSource
}
TableModel {
id:table_model
TableModelColumn {}
} }
TableModel{ TableModel{
id:header_column_model id: header_column_model
TableModelColumn {} TableModelColumn { display : "title"}
} }
TableModel{ TableModel{
id:header_row_model id: header_row_model
TableModelColumn { display: "rowIndex" } TableModelColumn { display: "rowIndex" }
} }
FluTableSortProxyModel{ FluTableSortProxyModel{
id:table_sort_model id: table_sort_model
model: table_model model: control.sourceModel
} }
Component{ Component{
id:com_edit id:com_edit
FluTextBox{ FluTextBox{
id:text_box id:text_box
text: String(display) text: String(display)
readOnly: true === d.columns_data[column].readOnly readOnly: true === control.columnSource[column].readOnly
Component.onCompleted: { Component.onCompleted: {
forceActiveFocus() forceActiveFocus()
selectAll() selectAll()
@ -113,7 +142,7 @@ Rectangle {
TextArea.flickable: FluMultilineTextBox { TextArea.flickable: FluMultilineTextBox {
id:text_box id:text_box
text: String(display) text: String(display)
readOnly: true === d.columns_data[column].readOnly readOnly: true === control.columnSource[column].readOnly
verticalAlignment: TextInput.AlignVCenter verticalAlignment: TextInput.AlignVCenter
isCtrlEnterForNewline: true isCtrlEnterForNewline: true
Component.onCompleted: { Component.onCompleted: {
@ -196,19 +225,42 @@ Rectangle {
id:com_table_delegate id:com_table_delegate
MouseArea{ MouseArea{
id:item_table_mouse id:item_table_mouse
property var _model: model
property bool isMainTable: TableView.view == table_view
property var currentTableView: TableView.view
property bool isHide: {
if(isMainTable && columnModel.frozen){
return true
}
if(!isMainTable){
if(currentTableView.dataIndex !== columnModel.dataIndex)
return true
}
return false
}
property bool isRowSelected: {
if(!rowModel)
return false
if(d.current){
return rowModel._key === d.current._key
}
return false
}
property bool editVisible: {
if(!rowModel)
return false
if(d.editPosition && d.editPosition._key === rowModel._key && d.editPosition.column === column){
return true
}
return false
}
implicitWidth: isHide ? Number.MIN_VALUE : TableView.view.width
visible: !isHide
TableView.onPooled: { TableView.onPooled: {
if(d.editPosition && d.editPosition.row === row && d.editPosition.column === column){ if(d.editPosition && d.editPosition.row === row && d.editPosition.column === column){
control.closeEditor() control.closeEditor()
} }
} }
property var rowObject : control.getRow(row)
property var itemModel: model
property bool editVisible: {
if(rowObject && d.editPosition && d.editPosition._key === rowObject._key && d.editPosition.column === column){
return true
}
return false
}
hoverEnabled: true hoverEnabled: true
onEntered: { onEntered: {
d.rowHoverIndex = row d.rowHoverIndex = row
@ -217,25 +269,37 @@ Rectangle {
if(editVisible){ if(editVisible){
updateEditPosition() updateEditPosition()
} }
if(isMainTable){
updateTableItem()
}
} }
onHeightChanged: { onHeightChanged: {
if(editVisible){ if(editVisible){
updateEditPosition() updateEditPosition()
} }
if(isMainTable){
updateTableItem()
}
} }
onXChanged: { onXChanged: {
if(editVisible){ if(editVisible){
updateEditPosition() updateEditPosition()
} }
if(isMainTable){
updateTableItem()
}
} }
onYChanged: { onYChanged: {
if(editVisible){ if(editVisible){
updateEditPosition() updateEditPosition()
} }
if(isMainTable){
updateTableItem()
}
} }
function updateEditPosition(){ function updateEditPosition(){
var obj = {} var obj = {}
obj._key = rowObject._key obj._key = rowModel._key
obj.column = column obj.column = column
obj.row = row obj.row = row
obj.x = item_table_mouse.x obj.x = item_table_mouse.x
@ -244,26 +308,22 @@ Rectangle {
obj.height = item_table_mouse.height - 2 obj.height = item_table_mouse.height - 2
d.editPosition = obj d.editPosition = obj
} }
function updateTableItem(){
var columnModel = control.columnSource[column]
columnModel.x = item_table_mouse.x
columnModel.y = item_table_mouse.y
d.tableItemLayout(column)
}
Rectangle{ Rectangle{
id:item_table
anchors.fill: parent anchors.fill: parent
property point position: Qt.point(column,row)
property bool isRowSelected: {
if(rowObject === null)
return false
if(d.current){
return rowObject._key === d.current._key
}
return false
}
color:{ color:{
if(item_table.isRowSelected){ if(item_table_mouse.isRowSelected){
return control.selectedColor return control.selectedColor
} }
if(d.rowHoverIndex === row || item_table.isRowSelected){ if(d.rowHoverIndex === row || item_table_mouse.isRowSelected){
return FluTheme.dark ? Qt.rgba(1,1,1,0.06) : Qt.rgba(0,0,0,0.06) return FluTheme.dark ? Qt.rgba(1,1,1,0.06) : Qt.rgba(0,0,0,0.06)
} }
return (row%2!==0) ? control.color : (FluTheme.dark ? Qt.rgba(1,1,1,0.015) : Qt.rgba(0,0,0,0.015)) return (row%2!==0) ? control.color : (FluTheme.dark ? Qt.rgba(1,1,1,0.03) : Qt.rgba(0,0,0,0.03))
} }
MouseArea{ MouseArea{
anchors.fill: parent anchors.fill: parent
@ -276,25 +336,28 @@ Rectangle {
onReleased: { onReleased: {
} }
onDoubleClicked:{ onDoubleClicked:{
if(typeof(display) == "object"){ if(item_table_loader.isObject){
return return
} }
loader_edit.display = display loader_edit.display = item_table_loader.display
d.editDelegate = d.getEditDelegate(column) d.editDelegate = d.getEditDelegate(column)
updateEditPosition() item_table_mouse.updateEditPosition()
} }
onClicked: onClicked:
(event)=>{ (event)=>{
d.current = rowObject d.current = rowModel
control.closeEditor() control.closeEditor()
event.accepted = true event.accepted = true
} }
} }
FluLoader{ FluLoader{
property var model: itemModel id: item_table_loader
property var display: itemModel.display property var model: item_table_mouse._model
property int row: item_table.position.y property var display: rowModel[columnModel.dataIndex]
property int column: item_table.position.x 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 bool isObject: typeof(display) == "object"
property var options: { property var options: {
if(isObject){ if(isObject){
@ -304,15 +367,53 @@ Rectangle {
} }
anchors.fill: parent anchors.fill: parent
sourceComponent: { sourceComponent: {
if(isObject){ if(item_table_mouse.visible){
return display.comId 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{ Item{
anchors.fill: parent anchors.fill: parent
visible: item_table.isRowSelected visible: item_table_mouse.isRowSelected
Rectangle{ Rectangle{
width: 1 width: 1
height: parent.height height: parent.height
@ -343,6 +444,11 @@ Rectangle {
} }
} }
} }
onWidthChanged:{
table_view.forceLayout()
}
MouseArea{ MouseArea{
id:layout_mouse_table id:layout_mouse_table
hoverEnabled: true hoverEnabled: true
@ -364,30 +470,8 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
ScrollBar.horizontal:scroll_bar_h ScrollBar.horizontal:scroll_bar_h
ScrollBar.vertical:scroll_bar_v ScrollBar.vertical:scroll_bar_v
columnWidthProvider: function(column) { columnWidthProvider: control.columnWidthProvider
var columnObject = d.columns_data[column] rowHeightProvider: control.rowHeightProvider
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
}
model: table_sort_model model: table_sort_model
clip: true clip: true
onRowsChanged: { onRowsChanged: {
@ -395,70 +479,51 @@ Rectangle {
table_view.flick(0,1) table_view.flick(0,1)
} }
delegate: com_table_delegate 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{ Component{
id:com_column_header_delegate id: com_column_header_delegate
Rectangle{ Rectangle{
id:column_item_control id: column_item_control
property var currentTableView : TableView.view
readonly property real cellPadding: 8 readonly property real cellPadding: 8
property bool canceled: false property bool canceled: false
property int columnIndex: column property var _model: model
readonly property var columnObject : d.columns_data[column] readonly property var columnModel : control.columnSource[_index]
implicitWidth: { readonly property int _index : {
return (item_column_loader.item && item_column_loader.item.implicitWidth) + (cellPadding * 2) 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) color: FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(247/255,247/255,247/255,1)
Rectangle{ Rectangle{
border.color: control.borderColor border.color: control.borderColor
@ -479,7 +544,7 @@ Rectangle {
width: 1 width: 1
height: parent.height height: parent.height
anchors.left: parent.left anchors.left: parent.left
visible: column !== 0 visible: column_item_control._index !== 0
color:"#00000000" color:"#00000000"
} }
Rectangle{ Rectangle{
@ -488,7 +553,7 @@ Rectangle {
height: parent.height height: parent.height
anchors.right: parent.right anchors.right: parent.right
color:"#00000000" color:"#00000000"
visible: column === table_view.columns - 1 visible: column_item_control._index === table_view.columns - 1
} }
MouseArea{ MouseArea{
id:column_item_control_mouse id:column_item_control_mouse
@ -510,22 +575,23 @@ Rectangle {
} }
FluLoader{ FluLoader{
id:item_column_loader id:item_column_loader
property var itemModel: model property var model: column_item_control._model
property var modelData: model.display property var display: model.display.title
property var tableView: table_view property var tableView: table_view
property var tableModel: table_model property var sourceModel: control.sourceModel
property bool isObject: typeof(display) == "object"
property var options:{ property var options:{
if(typeof(modelData) == "object"){ if(isObject){
return modelData.options return display.options
} }
return {} return {}
} }
property int column: column_item_control.columnIndex property int column: column_item_control._index
width: parent.width width: parent.width
height: parent.height height: parent.height
sourceComponent: { sourceComponent: {
if(typeof(modelData) == "object"){ if(isObject){
return modelData.comId return display.comId
} }
return com_column_text return com_column_text
} }
@ -537,7 +603,7 @@ Rectangle {
anchors.right: parent.right anchors.right: parent.right
acceptedButtons: Qt.LeftButton acceptedButtons: Qt.LeftButton
hoverEnabled: true 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 cursorShape: Qt.SplitHCursor
preventStealing: true preventStealing: true
onPressed : onPressed :
@ -557,9 +623,9 @@ Rectangle {
return return
} }
var delta = Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y) var delta = Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y)
var minimumWidth = columnObject.minimumWidth var minimumWidth = columnModel.minimumWidth
var maximumWidth = columnObject.maximumWidth var maximumWidth = columnModel.maximumWidth
var w = columnObject.width var w = columnModel.width
if(!w){ if(!w){
w = d.defaultItemWidth w = d.defaultItemWidth
} }
@ -569,7 +635,7 @@ Rectangle {
if(!maximumWidth){ if(!maximumWidth){
maximumWidth = 65535 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() table_view.forceLayout()
header_horizontal.forceLayout() header_horizontal.forceLayout()
} }
@ -582,7 +648,7 @@ Rectangle {
id:item_control id:item_control
readonly property real cellPadding: 8 readonly property real cellPadding: 8
property bool canceled: false 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)) implicitWidth: Math.max(30, row_text.implicitWidth + (cellPadding * 2))
implicitHeight: row_text.implicitHeight + (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) 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 cursorShape: Qt.SplitVCursor
preventStealing: true preventStealing: true
visible: { visible: {
if(rowObject === null) if(rowModel === null)
return false 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 : onPressed :
(mouse)=>{ (mouse)=>{
@ -668,11 +734,11 @@ Rectangle {
if(!pressed){ if(!pressed){
return return
} }
var rowObject = control.getRow(row) var rowModel = control.getRow(row)
var delta = Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y) var delta = Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y)
var minimumHeight = rowObject._minimumHeight var minimumHeight = rowModel._minimumHeight
var maximumHeight = rowObject._maximumHeight var maximumHeight = rowModel._maximumHeight
var h = rowObject.height var h = rowModel.height
if(!h){ if(!h){
h = d.defaultItemHeight h = d.defaultItemHeight
} }
@ -682,8 +748,8 @@ Rectangle {
if(!maximumHeight){ if(!maximumHeight){
maximumHeight = 65535 maximumHeight = 65535
} }
rowObject.height = Math.min(Math.max(minimumHeight, h + delta.y),maximumHeight) rowModel.height = Math.min(Math.max(minimumHeight, h + delta.y),maximumHeight)
control.setRow(row,rowObject) control.setRow(row,rowModel)
table_view.forceLayout() table_view.forceLayout()
} }
} }
@ -693,7 +759,7 @@ Rectangle {
id:com_column_text id:com_column_text
FluText { FluText {
id: column_text id: column_text
text: modelData text: String(display)
anchors.fill: parent anchors.fill: parent
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter 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<control.columnSource.length;i++){
item = control.columnSource[i]
if(item.frozen){
maxX = maxX - table_view.columnWidthProvider(i)
}
}
return Math.min(Math.max(columnModel.x - table_view.contentX,minX),maxX)}
)
}
Component.onCompleted: {
updateLayout()
}
height: control.height
visible: !item_layout_frozen.isHide
sourceComponent: item_layout_frozen.isHide ? undefined : com_table_frozen
}
}
}
FluScrollBar { FluScrollBar {
id: scroll_bar_h id: scroll_bar_h
anchors{ anchors{
@ -842,7 +1040,7 @@ Rectangle {
function sort(callback=undefined){ function sort(callback=undefined){
if(callback){ if(callback){
table_sort_model.setComparator(function(left,right){ table_sort_model.setComparator(function(left,right){
return callback(table_model.getRow(left),table_model.getRow(right)) return callback(sourceModel.getRow(left),sourceModel.getRow(right))
}) })
}else{ }else{
table_sort_model.setComparator(undefined) table_sort_model.setComparator(undefined)
@ -851,7 +1049,7 @@ Rectangle {
function filter(callback=undefined){ function filter(callback=undefined){
if(callback){ if(callback){
table_sort_model.setFilter(function(index){ table_sort_model.setFilter(function(index){
return callback(table_model.getRow(index)) return callback(sourceModel.getRow(index))
}) })
}else{ }else{
table_sort_model.setFilter(undefined) table_sort_model.setFilter(undefined)
@ -873,7 +1071,26 @@ Rectangle {
table_view.model.removeRow(rowIndex,rows) table_view.model.removeRow(rowIndex,rows)
} }
} }
function insertRow(rowIndex,obj){
if(rowIndex>=0 && rowIndex<table_view.rows){
sourceModel.insertRow(rowIndex,obj)
}
}
function currentIndex(){
var index = -1
if(!d.current){
return index
}
for (var i = 0; i <= sourceModel.rowCount-1; i++) {
var sourceItem = sourceModel.getRow(i);
if(sourceItem._key === d.current._key){
index = i
break
}
}
return index
}
function appendRow(obj){ function appendRow(obj){
table_model.appendRow(obj) sourceModel.appendRow(obj)
} }
} }

View File

@ -17,6 +17,7 @@ Rectangle {
property color selectedBorderColor: FluTheme.primaryColor property color selectedBorderColor: FluTheme.primaryColor
property color selectedColor: FluTools.withOpacity(FluTheme.primaryColor,0.3) property color selectedColor: FluTools.withOpacity(FluTheme.primaryColor,0.3)
readonly property alias current: d.current readonly property alias current: d.current
property alias view: table_view
id:control id:control
color: { color: {
if(Window.active){ if(Window.active){
@ -28,21 +29,26 @@ Rectangle {
tree_model.setDataSource(dataSource) tree_model.setDataSource(dataSource)
} }
onColumnSourceChanged: { onColumnSourceChanged: {
var columns= [] if(columnSource.length !== 0){
var headerRow = {} var columns= []
columnSource.forEach(function(item){ var headerRow = {}
var column = Qt.createQmlObject('import Qt.labs.qmlmodels 1.0;TableModelColumn{}',control); columnSource.forEach(function(item){
column.display = item.dataIndex var column = Qt.createQmlObject('import Qt.labs.qmlmodels 1.0;TableModelColumn{}',control);
columns.push(column) column.display = item.dataIndex
headerRow[item.dataIndex] = item.title columns.push(column)
}) headerRow[item.dataIndex] = item.title
header_column_model.columns = columns })
header_column_model.rows = [headerRow] header_column_model.columns = columns
header_column_model.rows = [headerRow]
}
} }
FluTreeModel{ FluTreeModel{
id:tree_model id:tree_model
columnSource: control.columnSource columnSource: control.columnSource
} }
Component.onDestruction: {
table_view.contentY = 0
}
onDepthPaddingChanged: { onDepthPaddingChanged: {
table_view.forceLayout() table_view.forceLayout()
} }

View File

@ -59,8 +59,9 @@ Window {
signal lazyLoad() signal lazyLoad()
property var _windowRegister property var _windowRegister
property string _route property string _route
id:window property bool _hideShadow: false
color:"transparent" id: window
color: FluTools.isSoftware() ? window.backgroundColor : "transparent"
Component.onCompleted: { Component.onCompleted: {
FluRouter.addWindow(window) FluRouter.addWindow(window)
useSystemAppBar = FluApp.useSystemAppBar useSystemAppBar = FluApp.useSystemAppBar
@ -120,12 +121,21 @@ Window {
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
asynchronous: true asynchronous: true
Component.onCompleted: { Component.onCompleted: {
var geometry = FluTools.desktopAvailableGeometry(window) img_back.updateLayout()
width = geometry.width
height = geometry.height
sourceSize = Qt.size(width,height)
source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath) source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath)
} }
Connections{
target: window
function onScreenChanged(){
img_back.updateLayout()
}
}
function updateLayout(){
var geometry = FluTools.desktopAvailableGeometry(window)
img_back.width = geometry.width
img_back.height = geometry.height
img_back.sourceSize = Qt.size(img_back.width,img_back.height)
}
Connections{ Connections{
target: FluTheme target: FluTheme
function onDesktopImagePathChanged(){ function onDesktopImagePathChanged(){
@ -141,7 +151,7 @@ Window {
} }
Timer{ Timer{
id:timer_update_image id:timer_update_image
interval: 500 interval: 150
onTriggered: { onTriggered: {
img_back.source = "" img_back.source = ""
img_back.source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath) img_back.source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath)
@ -155,7 +165,7 @@ Window {
blurRadius: 64 blurRadius: 64
visible: window.active && FluTheme.blurBehindWindowEnabled visible: window.active && FluTheme.blurBehindWindowEnabled
tintColor: FluTheme.dark ? Qt.rgba(0, 0, 0, 1) : Qt.rgba(1, 1, 1, 1) tintColor: FluTheme.dark ? Qt.rgba(0, 0, 0, 1) : Qt.rgba(1, 1, 1, 1)
targetRect: Qt.rect(window.x,window.y,window.width,window.height) targetRect: Qt.rect(window.x-window.screen.virtualX,window.y-window.screen.virtualY,window.width,window.height)
} }
} }
} }
@ -303,7 +313,6 @@ Window {
return info_bar.clearAllInfo() return info_bar.clearAllInfo()
} }
function moveWindowToDesktopCenter(){ function moveWindowToDesktopCenter(){
screen = Qt.application.screens[FluTools.cursorScreenIndex()]
var availableGeometry = FluTools.desktopAvailableGeometry(window) var availableGeometry = FluTools.desktopAvailableGeometry(window)
window.setGeometry((availableGeometry.width-window.width)/2+Screen.virtualX,(availableGeometry.height-window.height)/2+Screen.virtualY,window.width,window.height) window.setGeometry((availableGeometry.width-window.width)/2+Screen.virtualX,(availableGeometry.height-window.height)/2+Screen.virtualY,window.width,window.height)
} }
@ -340,4 +349,7 @@ Window {
function setHitTestVisible(val){ function setHitTestVisible(val){
frameless.setHitTestVisible(val) frameless.setHitTestVisible(val)
} }
function deleteLater(){
FluTools.deleteLater(window)
}
} }

View File

@ -74,49 +74,49 @@
<name>FluColorPicker</name> <name>FluColorPicker</name>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="16"/> <location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="16"/>
<location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="16"/> <location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="17"/>
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="17"/> <location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="17"/>
<location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="17"/> <location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="18"/>
<source>OK</source> <source>OK</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="18"/> <location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="18"/>
<location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="18"/> <location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="19"/>
<source>Color Picker</source> <source>Color Picker</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="19"/> <location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="19"/>
<location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="19"/> <location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="20"/>
<source>Edit Color</source> <source>Edit Color</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="20"/> <location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="20"/>
<location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="20"/> <location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="21"/>
<source>Red</source> <source>Red</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="21"/> <location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="21"/>
<location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="21"/> <location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="22"/>
<source>Green</source> <source>Green</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="22"/> <location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="22"/>
<location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="22"/> <location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="23"/>
<source>Blue</source> <source>Blue</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="23"/> <location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="23"/>
<location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="23"/> <location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="24"/>
<source>Opacity</source> <source>Opacity</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -187,14 +187,14 @@
<context> <context>
<name>FluPagination</name> <name>FluPagination</name>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluPagination.qml" line="8"/> <location filename="Qt5/imports/FluentUI/Controls/FluPagination.qml" line="10"/>
<location filename="Qt6/imports/FluentUI/Controls/FluPagination.qml" line="8"/> <location filename="Qt6/imports/FluentUI/Controls/FluPagination.qml" line="9"/>
<source>&lt;Previous</source> <source>&lt;Previous</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluPagination.qml" line="9"/> <location filename="Qt5/imports/FluentUI/Controls/FluPagination.qml" line="11"/>
<location filename="Qt6/imports/FluentUI/Controls/FluPagination.qml" line="9"/> <location filename="Qt6/imports/FluentUI/Controls/FluPagination.qml" line="10"/>
<source>Next&gt;</source> <source>Next&gt;</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -231,6 +231,11 @@
<source>Reset</source> <source>Reset</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="Qt6/imports/FluentUI/Controls/FluShortcutPicker.qml" line="205"/>
<source>Conflict</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>FluStatusLayout</name> <name>FluStatusLayout</name>
@ -349,10 +354,23 @@
<context> <context>
<name>FluWindow</name> <name>FluWindow</name>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluWindow.qml" line="335"/> <location filename="Qt5/imports/FluentUI/Controls/FluWindow.qml" line="347"/>
<location filename="Qt6/imports/FluentUI/Controls/FluWindow.qml" line="334"/> <location filename="Qt6/imports/FluentUI/Controls/FluWindow.qml" line="346"/>
<source>Loading...</source> <source>Loading...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>QHotkey</name>
<message>
<location filename="qhotkey/qhotkey.cpp" line="294"/>
<source>Failed to register %1. Error: %2</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qhotkey/qhotkey.cpp" line="314"/>
<source>Failed to unregister %1. Error: %2</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS> </TS>

View File

@ -74,49 +74,49 @@
<name>FluColorPicker</name> <name>FluColorPicker</name>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="16"/> <location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="16"/>
<location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="16"/> <location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="17"/>
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="17"/> <location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="17"/>
<location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="17"/> <location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="18"/>
<source>OK</source> <source>OK</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="18"/> <location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="18"/>
<location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="18"/> <location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="19"/>
<source>Color Picker</source> <source>Color Picker</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="19"/> <location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="19"/>
<location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="19"/> <location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="20"/>
<source>Edit Color</source> <source>Edit Color</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="20"/> <location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="20"/>
<location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="20"/> <location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="21"/>
<source>Red</source> <source>Red</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="21"/> <location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="21"/>
<location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="21"/> <location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="22"/>
<source>Green</source> <source>Green</source>
<translation type="unfinished">绿</translation> <translation type="unfinished">绿</translation>
</message> </message>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="22"/> <location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="22"/>
<location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="22"/> <location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="23"/>
<source>Blue</source> <source>Blue</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="23"/> <location filename="Qt5/imports/FluentUI/Controls/FluColorPicker.qml" line="23"/>
<location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="23"/> <location filename="Qt6/imports/FluentUI/Controls/FluColorPicker.qml" line="24"/>
<source>Opacity</source> <source>Opacity</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -187,14 +187,14 @@
<context> <context>
<name>FluPagination</name> <name>FluPagination</name>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluPagination.qml" line="8"/> <location filename="Qt5/imports/FluentUI/Controls/FluPagination.qml" line="10"/>
<location filename="Qt6/imports/FluentUI/Controls/FluPagination.qml" line="8"/> <location filename="Qt6/imports/FluentUI/Controls/FluPagination.qml" line="9"/>
<source>&lt;Previous</source> <source>&lt;Previous</source>
<translation type="unfinished">&lt;</translation> <translation type="unfinished">&lt;</translation>
</message> </message>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluPagination.qml" line="9"/> <location filename="Qt5/imports/FluentUI/Controls/FluPagination.qml" line="11"/>
<location filename="Qt6/imports/FluentUI/Controls/FluPagination.qml" line="9"/> <location filename="Qt6/imports/FluentUI/Controls/FluPagination.qml" line="10"/>
<source>Next&gt;</source> <source>Next&gt;</source>
<translation type="unfinished">&gt;</translation> <translation type="unfinished">&gt;</translation>
</message> </message>
@ -231,6 +231,11 @@
<source>Reset</source> <source>Reset</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="Qt6/imports/FluentUI/Controls/FluShortcutPicker.qml" line="205"/>
<source>Conflict</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>FluStatusLayout</name> <name>FluStatusLayout</name>
@ -349,10 +354,23 @@
<context> <context>
<name>FluWindow</name> <name>FluWindow</name>
<message> <message>
<location filename="Qt5/imports/FluentUI/Controls/FluWindow.qml" line="335"/> <location filename="Qt5/imports/FluentUI/Controls/FluWindow.qml" line="347"/>
<location filename="Qt6/imports/FluentUI/Controls/FluWindow.qml" line="334"/> <location filename="Qt6/imports/FluentUI/Controls/FluWindow.qml" line="346"/>
<source>Loading...</source> <source>Loading...</source>
<translation type="unfinished">...</translation> <translation type="unfinished">...</translation>
</message> </message>
</context> </context>
<context>
<name>QHotkey</name>
<message>
<location filename="qhotkey/qhotkey.cpp" line="294"/>
<source>Failed to register %1. Error: %2</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qhotkey/qhotkey.cpp" line="314"/>
<source>Failed to unregister %1. Error: %2</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS> </TS>

377
src/qhotkey/qhotkey.cpp Normal file
View File

@ -0,0 +1,377 @@
#include "qhotkey.h"
#include "qhotkey_p.h"
#include <QCoreApplication>
#include <QAbstractEventDispatcher>
#include <QMetaMethod>
#include <QThread>
#include <QDebug>
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<int>(_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);
}

130
src/qhotkey/qhotkey.h Normal file
View File

@ -0,0 +1,130 @@
#ifndef QHOTKEY_H
#define QHOTKEY_H
#include <QObject>
#include <QKeySequence>
#include <QPair>
#include <QLoggingCategory>
#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

291
src/qhotkey/qhotkey_mac.cpp Normal file
View File

@ -0,0 +1,291 @@
#include "qhotkey.h"
#include "qhotkey_p.h"
#include <Carbon/Carbon.h>
#include <QDebug>
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<QHotkey::NativeShortcut, EventHotKeyRef> hotkeyRefs;
};
NATIVE_INSTANCE(QHotkeyPrivateMac)
bool QHotkeyPrivate::isPlatformSupported()
{
return true;
}
bool QHotkeyPrivateMac::isHotkeyHandlerRegistered = false;
QHash<QHotkey::NativeShortcut, EventHotKeyRef> 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<UCKeyStateRecordsIndex*>(data + table[i].keyStateRecordsIndexOffset);
if (stateRec->keyStateRecordsIndexFormat != kUCKeyStateRecordsIndexFormat) stateRec = 0;
}
UCKeyToCharTableIndex* charTable = reinterpret_cast<UCKeyToCharTableIndex*>(data + table[i].keyToCharTableIndexOffset);
if (charTable->keyToCharTableIndexFormat != kUCKeyToCharTableIndexFormat) continue;
for (quint32 j=0; j < charTable->keyToCharTableCount; j++) {
UCKeyOutput* keyToChar = reinterpret_cast<UCKeyOutput*>(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<UCKeyStateRecord*>(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;
}

62
src/qhotkey/qhotkey_p.h Normal file
View File

@ -0,0 +1,62 @@
#ifndef QHOTKEY_P_H
#define QHOTKEY_P_H
#include "qhotkey.h"
#include <QAbstractNativeEventFilter>
#include <QMultiHash>
#include <QMutex>
#include <QGlobalStatic>
#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<QPair<Qt::Key, Qt::KeyboardModifiers>, QHotkey::NativeShortcut> mapping;
QMultiHash<QHotkey::NativeShortcut, QHotkey*> 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

309
src/qhotkey/qhotkey_win.cpp Normal file
View File

@ -0,0 +1,309 @@
#include "qhotkey.h"
#include "qhotkey_p.h"
#include <qt_windows.h>
#include <algorithm>
#include <QDebug>
#include <QList>
#include <QTimer>
#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<QHotkey::NativeShortcut> 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<MSG*>(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<WCHAR>(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<BYTE>(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();
}

268
src/qhotkey/qhotkey_x11.cpp Normal file
View File

@ -0,0 +1,268 @@
#include "qhotkey.h"
#include "qhotkey_p.h"
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
#include <QGuiApplication>
#else
#include <QDebug>
#include <QX11Info>
#endif
#include <QThreadStorage>
#include <QTimer>
#include <X11/Xlib.h>
#include <xcb/xcb.h>
//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<quint32> 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<QNativeInterface::QX11Application>();
#else
return QX11Info::isPlatformX11();
#endif
}
const QVector<quint32> 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<xcb_generic_event_t *>(message);
if (genericEvent->response_type == XCB_KEY_PRESS) {
xcb_key_press_event_t keyEvent = *static_cast<xcb_key_press_event_t *>(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<xcb_key_release_event_t *>(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<QNativeInterface::QX11Application>();
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<QNativeInterface::QX11Application>();
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<QNativeInterface::QX11Application>()->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;
}
}