diff --git a/CMakeLists.txt b/CMakeLists.txt
index f0402239..9d416420 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,6 +13,7 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/.cmake/)
include(GetGitRevisionDescription)
option(FLUENTUI_BUILD_EXAMPLES "Build FluentUI demo applications." ON)
+set(FLUENTUI_BUILD_STATIC_LIB OFF)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)
diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt
index e0528d8f..866312d7 100644
--- a/example/CMakeLists.txt
+++ b/example/CMakeLists.txt
@@ -4,6 +4,10 @@ project(example VERSION 1.0 LANGUAGES CXX)
#配置通用编译
set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+set(CMAKE_AUTOUIC ON)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
#判断FluentUI库类型
@@ -24,19 +28,11 @@ endif()
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Quick Svg Network)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Quick Svg Network)
-if(QT_VERSION VERSION_GREATER_EQUAL "6.3")
- qt_standard_project_setup()
-else()
- set(CMAKE_AUTOMOC ON)
- set(CMAKE_AUTORCC ON)
- set(CMAKE_AUTOUIC ON)
-endif()
-
-#国际化
+#添加国际化脚本
find_program(QT_LUPDATE NAMES lupdate)
find_program(QT_LRELEASE NAMES lrelease)
file(GLOB TS_FILE_PATHS ${CMAKE_CURRENT_LIST_DIR}/ *.ts)
-add_custom_target(UpdateTranslations
+add_custom_target(Script-UpdateTranslations
COMMAND ${QT_LUPDATE} ${CMAKE_CURRENT_LIST_DIR} -ts ${PROJECT_NAME}_en_US.ts WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
COMMAND ${QT_LUPDATE} ${CMAKE_CURRENT_LIST_DIR} -ts ${PROJECT_NAME}_zh_CN.ts WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
COMMAND ${QT_LRELEASE} ${PROJECT_NAME}_en_US.ts WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
@@ -64,30 +60,6 @@ if(WIN32)
list(APPEND sources_files "src/app_dmp.h")
endif()
-if(QT_VERSION VERSION_GREATER_EQUAL "6.2")
- #遍历所有qml文件
- file(GLOB_RECURSE QML_PATHS *.qml qmldir)
- foreach(filepath ${QML_PATHS})
- string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath})
- if(${filepath} MATCHES "qml-Qt6")
- string(REPLACE "qml-Qt6" "qml" filealias ${filename})
- set_source_files_properties(${filename} PROPERTIES QT_RESOURCE_ALIAS ${filealias})
- if(${filename} MATCHES "qmldir")
- list(APPEND resource_files ${filename})
- else()
- list(APPEND qml_files ${filename})
- endif()
- endif()
- endforeach(filepath)
-
- #遍历所有资源文件
- file(GLOB_RECURSE RES_PATHS *.png *.jpg *.svg *.ico *.ttf *.webp *.in)
- foreach(filepath ${RES_PATHS})
- string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath})
- list(APPEND resource_files ${filename})
- endforeach(filepath)
-endif()
-
#如果是Windows平台,则生成rc文件,还有inno setup脚本文件
set(EXAMPLE_VERSION_RC_PATH "")
if(WIN32)
@@ -103,21 +75,26 @@ configure_file(
endif()
#添加可执行文件
-if (WIN32)
- add_executable(${PROJECT_NAME}
+list(APPEND sources_files ${PROJECT_NAME}.qrc)
+if(WIN32)
+ list(APPEND sources_files ${EXAMPLE_VERSION_RC_PATH})
+endif()
+if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
+ qt_add_executable(${PROJECT_NAME}
+ MANUAL_FINALIZATION
${sources_files}
- ${EXAMPLE_VERSION_RC_PATH}
)
-else ()
+else()
add_executable(${PROJECT_NAME}
${sources_files}
)
-endif ()
-
-add_dependencies(${PROJECT_NAME} UpdateTranslations)
+endif()
+add_dependencies(${PROJECT_NAME} Script-UpdateTranslations)
#复制程序运行所需要的动态库
if(WIN32)
+ message("-----------------------------")
+ message(${CMAKE_CXX_COMPILER_ID})
if(MSVC)
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
file(GLOB_RECURSE 3RDPARTY_DLL_DIR ${CMAKE_SOURCE_DIR}/3rdparty/msvc/x86/*.dll)
@@ -134,23 +111,6 @@ endif()
file(MAKE_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/source/)
file(COPY ${CMAKE_SOURCE_DIR}/src/ DESTINATION ${APPLICATION_DIR_PATH}/source/)
-if(QT_VERSION VERSION_GREATER_EQUAL "6.2")
- #如果是Qt6.2以上,则使用qt_add_qml_module添加资源文件
- qt_add_qml_module(${PROJECT_NAME}
- URI ${PROJECT_NAME}
- VERSION 1.0
- QML_FILES ${qml_files}
- RESOURCES ${resource_files}
- RESOURCE_PREFIX "/"
- )
-else()
- #如果是Qt6.2以下,则使用qrc添加资源文件
- target_include_directories(${PROJECT_NAME} PRIVATE
- ${CMAKE_CURRENT_SOURCE_DIR}
- )
-target_sources(${PROJECT_NAME} PRIVATE ${PROJECT_NAME}.qrc)
-endif()
-
#导入component头文件,不然通过QML_NAMED_ELEMENT生成的c++类会找不到头文件报错
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/component
@@ -186,9 +146,28 @@ target_link_libraries(${PROJECT_NAME} PRIVATE
fluentuiplugin
)
-#安装
-install(TARGETS ${PROJECT_NAME}
- BUNDLE DESTINATION .
- LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
- RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-)
+#添加部署脚本
+if (CMAKE_BUILD_TYPE MATCHES "Release")
+ if (APPLE)
+ find_program(QT_DEPLOY_QT NAMES macdeployqt)
+ add_custom_target(Script-DeployRelease
+ COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_SOURCE_DIR}/dist
+ COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${CMAKE_SOURCE_DIR}/dist
+ COMMAND ${QT_DEPLOY_QT} ${CMAKE_SOURCE_DIR}/dist/${PROJECT_NAME} -qmldir=${CMAKE_CURRENT_LIST_DIR}
+ COMMENT "MacOs Deploying Qt Dependencies After Build........."
+ SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+ endif()
+ if(WIN32)
+ find_program(QT_DEPLOY_QT NAMES windeployqt)
+ add_custom_target(Script-DeployRelease
+ COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_SOURCE_DIR}/dist
+ COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${CMAKE_SOURCE_DIR}/dist
+ COMMAND ${QT_DEPLOY_QT} ${CMAKE_SOURCE_DIR}/dist/${PROJECT_NAME}.exe -qmldir=${CMAKE_CURRENT_LIST_DIR}
+ COMMENT "Windows Deploying Qt Dependencies After Build........."
+ SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+endif()
+endif()
diff --git a/example/README.md b/example/README.md
new file mode 100644
index 00000000..2ac81422
--- /dev/null
+++ b/example/README.md
@@ -0,0 +1,18 @@
+# FluentUI 脚手架开发说明
+
+## 代码说明
+
+文件编码格式请用utf-8不带bom,代码中最好不要含有中文(包括注释),中文可能会改变文件格式导致编译失败,还会出现乱码,显示中文请用国际化,c++中用tr函数,qml中用qsTr函数
+
+### 脚本说明
+
+1. **Script-UpdateTranslations**
+
+ 用于更新ts与qm文件,当你的代码添加了tr或者qsTr函数后,执行这个脚本会更新ts文件,然后编写翻译后,再执行这个脚本,qm文件会更新生效
+
+2. **Script-DeployRelease**
+
+ 执行Qt的windeployqt或macdeployqt命令,这个脚本只在windows与macos才有,linux不支持
+
+
+
diff --git a/example/example_en_US.ts b/example/example_en_US.ts
index a354efb0..f39856a6 100644
--- a/example/example_en_US.ts
+++ b/example/example_en_US.ts
@@ -5,13 +5,11 @@
CodeExpander
-
-
@@ -20,25 +18,21 @@
CrashWindow
-
-
-
-
@@ -48,38 +42,31 @@
-
-
-
-
-
-
-
@@ -88,13 +75,11 @@
HotloadWindow
-
-
@@ -126,13 +111,11 @@
ItemsFooter
-
-
@@ -141,463 +124,386 @@
ItemsOriginal
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -607,26 +513,21 @@
-
-
-
-
-
@@ -634,126 +535,104 @@
MainWindow
-
-
+
-
-
-
-
+
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
-
-
+
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
@@ -784,13 +660,11 @@ Updated content:
SingleInstanceWindow
-
-
@@ -799,13 +673,11 @@ Updated content:
SingleTaskWindow
-
-
@@ -814,79 +686,66 @@ Updated content:
StandardWindow
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -895,7 +754,6 @@ Updated content:
T_Acrylic
-
@@ -904,25 +762,21 @@ Updated content:
T_Awesome
-
-
-
-
@@ -931,13 +785,11 @@ Updated content:
T_Badge
-
-
@@ -946,7 +798,6 @@ Updated content:
T_BarChart
-
@@ -955,13 +806,11 @@ Updated content:
T_BreadcrumbBar
-
-
@@ -970,7 +819,6 @@ Updated content:
T_BubbleChart
-
@@ -979,19 +827,16 @@ Updated content:
T_Buttons
-
-
-
@@ -1004,164 +849,131 @@ Updated content:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1170,7 +982,6 @@ Updated content:
T_CalendarPicker
-
@@ -1179,37 +990,31 @@ Updated content:
T_Captcha
-
-
-
-
-
-
@@ -1218,13 +1023,11 @@ Updated content:
T_Carousel
-
-
@@ -1233,45 +1036,37 @@ Updated content:
T_CheckBox
-
-
-
-
-
-
-
-
@@ -1280,13 +1075,11 @@ Updated content:
T_Clip
-
-
@@ -1295,61 +1088,51 @@ Updated content:
T_ColorPicker
-
-
-
-
-
-
-
-
-
-
@@ -1358,7 +1141,6 @@ Updated content:
T_ComboBox
-
@@ -1367,44 +1149,16 @@ Updated content:
T_DatePicker
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1413,13 +1167,11 @@ Updated content:
T_Dialog
-
-
@@ -1427,25 +1179,18 @@ Updated content:
-
-
-
-
-
-
-
@@ -1453,9 +1198,6 @@ Updated content:
-
-
-
@@ -1464,10 +1206,6 @@ Updated content:
-
-
-
-
@@ -1475,63 +1213,51 @@ Updated content:
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1540,25 +1266,21 @@ Updated content:
T_Expander
-
-
-
-
-
Check
-
-
Save As...
-
-
Doc
-
-
PDF
-
Show Menu Popup
-
MenuBar
-
Edit
-
Cut
-
Copy
-
Paste
-
Help
-
About
@@ -1852,13 +1522,11 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_MultiWindow
-
MultiWindow
-
<font color='red'>Standard</font> mode window,a new window is created every time
@@ -1868,41 +1536,31 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
-
-
-
-
-
Create Window
-
<font color='red'>SingleTask</font> mode window,If a window exists, this activates the window
-
<font color='red'>SingleInstance</font> mode window,If the window exists, destroy the window and create a new window
-
Create the window without carrying any parameters
-
Create a window with the parameter username: zhuzichu
-
Login Window Returned Password - >
@@ -1911,7 +1569,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Network
-
Network
@@ -1920,7 +1577,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Pagination
-
Pagination
@@ -1929,7 +1585,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_PieChart
-
Pie Chart
@@ -1938,55 +1593,46 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Pivot
-
Pivot
-
All
-
All emails go here.
-
Unread
-
Unread emails go here.
-
Flagged
-
Flagged emails go here.
-
Urgent
-
Urgent emails go here.
@@ -1995,7 +1641,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_PolarAreaChart
-
Polar Area Chart
@@ -2004,7 +1649,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Progress
-
Progress
@@ -2013,7 +1657,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_QRCode
-
QRCode
@@ -2022,7 +1665,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_RadarChart
-
Radar Chart
@@ -2031,45 +1673,37 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_RadioButton
-
RadioButton
-
Right
-
Left
-
-
Disabled
-
Radio Button_1
-
Radio Button_2
-
Radio Button_3
@@ -2078,7 +1712,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_RatingControl
-
RatingControl
@@ -2087,7 +1720,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Rectangle
-
Rectangle
@@ -2096,7 +1728,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_ScatterChart
-
Scatter Chart
@@ -2105,115 +1736,96 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Settings
-
Settings
-
Current Version
-
Check for Updates
-
Use System AppBar
-
Fits AppBar Windows
-
Friendly Reminder
-
This action requires a restart of the program to take effect, is it restarted?
-
Cancel
-
OK
-
Dark Mode
-
System
-
Light
-
Dark
-
Navigation View Display Mode
-
Open
-
Compact
-
Minimal
-
Auto
-
Language
@@ -2222,7 +1834,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_ShortcutPicker
-
ShortcutPicker
@@ -2231,7 +1842,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Slider
-
Slider
@@ -2240,7 +1850,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_SplitLayout
-
SplitLayout
@@ -2249,7 +1858,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_StaggeredLayout
-
StaggeredLayout
@@ -2258,31 +1866,26 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_StatusLayout
-
StatusLayout
-
Loading...
-
Empty
-
The page went wrong...
-
Reload
@@ -2291,13 +1894,11 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_TabView
-
TabView
-
Document
@@ -2306,123 +1907,102 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_TableView
-
TableView
-
Modify the column name
-
Cancel
-
OK
-
Search
-
-
Name
-
Delete
-
Edit
-
Select All
-
Age
-
Clear All
-
Avatar
-
Address
-
Nickname
-
Long String
-
Options
-
<Previous
-
Next>
-
Delete Selection
-
Add a row of Data
@@ -2431,13 +2011,11 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Text
-
Text
-
This is a text that can be copied
@@ -2446,13 +2024,11 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_TextBox
-
TextBox
-
Single-line Input Box
@@ -2462,29 +2038,21 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
-
-
-
-
-
Disabled
-
Please enter your password
-
Multi-line Input Box
-
AutoSuggestBox
@@ -2493,37 +2061,31 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Theme
-
Theme
-
Theme colors
-
Customize the Theme Color
-
Dark Mode
-
Native Text
-
Open Animation
@@ -2532,67 +2094,52 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_TimePicker
-
TimePicker
-
hourFormat=FluTimePickerType.H
-
-
AM
-
-
PM
-
-
Hour
-
-
Minute
-
-
Cancel
-
-
OK
-
hourFormat=FluTimePickerType.HH
@@ -2601,19 +2148,16 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Timeline
-
Timeline
-
Append
-
clear
@@ -2622,25 +2166,21 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_ToggleSwitch
-
ToggleSwitch
-
Right
-
Left
-
Disabled
@@ -2649,45 +2189,37 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Tooltip
-
Tooltip
-
Hover over Tultip and it pops up
-
Text properties of FluIconButton support the Tooltip pop-up window by default
-
-
Delete
-
Click IconButton
-
Add a Tooltip pop-up to a Button
-
Click Button
@@ -2696,13 +2228,11 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Tour
-
Upload File
-
Put your files here.
@@ -2710,53 +2240,42 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
-
-
-
Save
-
Save your changes.
-
Other Actions
-
Click to see other actions.
-
Begin Tour
-
-
Upload
-
More
-
Tour
@@ -2765,19 +2284,16 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_TreeView
-
TreeView
-
Total %1 data, %2 data currently displayed
-
A total of %1 data items are selected
@@ -2786,7 +2302,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Typography
-
Typography
@@ -2795,7 +2310,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Watermark
-
Watermark
diff --git a/example/example_zh_CN.ts b/example/example_zh_CN.ts
index 4f8ebdb6..a9c0ee7d 100644
--- a/example/example_zh_CN.ts
+++ b/example/example_zh_CN.ts
@@ -5,13 +5,11 @@
CodeExpander
-
Source
源码
-
The Copy is Successful
复制成功
@@ -20,25 +18,21 @@
CrashWindow
-
Friendly Reminder
友情提示
-
We apologize for the inconvenience caused by an unexpected error
对于意外错误给您带来的不便,我们深表歉意
-
Report Logs
上报日志
-
Restart Program
重启程序
@@ -48,38 +42,31 @@
-
-
FluentUI Initalizr
FluentUI脚手架
-
Name
名称
-
Create In
创建路径
-
Browse
浏览
-
Cancel
取消
-
Create
创建
@@ -88,13 +75,11 @@
HotloadWindow
-
Hot Loader
热加载
-
Drag in a qml file
拖入qml文件
@@ -126,13 +111,11 @@
ItemsFooter
-
About
关于
-
Settings
设置
@@ -141,463 +124,386 @@
ItemsOriginal
-
Home
首页
-
PaneItemExpander Disabled
PaneItemExpander 已禁用
-
Basic Input
基本输入
-
Buttons
按钮
-
A control that responds to user input and raisesa Click event.
响应用户输入并引发 Click 事件的控件。
-
Text
文本
-
Image
图像
-
Slider
滑块
-
A control that lets the user select from a rangeof values by moving a Thumb control along atrack.
一个控件,允许用户通过沿轨道移动 Thumb 控件从一系列值中进行选择。
-
CheckBox
复选框
-
A control that a user can select or clear.
用户可以选择或清除的控件。
-
RadioButton
单选按钮
-
ToggleSwitch
切换开关
-
PaneItem Disabled
PaneItem 已禁用
-
Form
表单
-
TextBox
文本框
-
TimePicker
时间选择器
-
DatePicker
日期选择器
-
CalendarPicker
日历选择器
-
ColorPicker
颜色选择器
-
ShortcutPicker
快捷键选择器
-
Surface
表面
-
InfoBar
信息栏
-
An inline message to display app-wide statuschange information.
用于显示应用范围状态更改信息的内联消息。
-
Progress
进度条
-
RatingControl
评级控制
-
Badge
徽章
-
Rectangle
矩形
-
Clip
裁剪
-
Carousel
轮播图
-
Expander
展开
-
Watermark
水印
-
Layout
布局
-
StaggeredLayout
瀑布流布局
-
SplitLayout
拆分布局
-
StatusLayout
状态布局
-
Popus
弹窗
-
Dialog
弹窗
-
ComboBox
组合框
-
Tooltip
工具提示
-
Menu
菜单
-
Navigation
导航
-
Pivot
轴转
-
Presents information from different sources in a tabbed view.
在选项卡式视图中显示来自不同源的信息。
-
BreadcrumbBar
面包屑
-
TabView
选项卡
-
A control that displays a collection of tabs thatcan be used to display several documents.
一个控件,用于显示可用于显示多个文档的选项卡集合。
-
TreeView
树
-
TableView
表格
-
The TableView control provides a flexible way to display a collection of data in rows and columns
TableView 控件提供了一种以行和列形式显示数据集合的灵活方法
-
Pagination
分页
-
MultiWindow
多窗口
-
FlipView
翻转视图
-
Presents a collection of items that the user canflip through, one item at a time.
显示用户可以翻阅的项集合,一次翻阅一个项。
-
Theming
主题
-
Acrylic
亚克力
-
Theme
主题
-
Typography
字体
-
Awesome
图标
-
Chart
图表
-
Bar Chart
条形图
-
Line Chart
线型图
-
Pie Chart
饼图
-
Polar Area Chart
极坐标区域图
-
Bubble Chart
气泡图
-
Scatter Chart
散点图
-
Radar Chart
雷达图
-
Other
其他
-
QRCode
二维码
-
Tour
游览
-
Timeline
时间轴
-
Captcha
验证码
-
Network
网络
-
Remote Loader
远程加载
-
Hot Loader
热加载
-
3D
3D
-
Test Crash
测试崩溃
@@ -607,26 +513,21 @@
-
-
Login
登录
-
Please enter the account
请输入账号
-
Please enter your password
请输入密码
-
Please feel free to enter a password
请随意输入一个密码
@@ -634,126 +535,104 @@
MainWindow
-
-
+
Dark Mode
夜间模式
-
-
-
-
+
+
Quit
退出
-
-
+
Are you sure you want to exit the program?
您确定要退出程序吗
-
-
+
Minimize
最小化
-
-
+
Friendly Reminder
友情提示
-
-
+
FluentUI is hidden from the tray, click on the tray to activate the window again
FluentUI 在托盘中处于隐藏状态,单击托盘以再次激活窗口
-
-
-
-
+
+
Cancel
取消
-
-
+
Open in Separate Window
在独立窗口中打开
-
-
+
Click Time
点击次数
-
-
+
Search
搜索
-
-
+
Finish
完成
-
-
+
Next
下一步
-
-
+
Previous
上一步
-
-
+
Here you can switch to night mode.
在这里,您可以切换到夜间模式。
-
-
+
Hide Easter eggs
隐藏彩蛋
-
-
+
Try a few more clicks!!
再试几下!!
-
-
+
Upgrade Tips
升级提示
-
-
+
FluentUI is currently up to date
FluentUI 目前最新版本
-
-
+
-- The current app version
-- 当前应用版本
-
-
+
Now go and download the new version?
@@ -766,20 +645,17 @@ Updated content:
-
-
+
OK
确定
-
-
+
The current version is already the latest
当前版本已经是最新版本
-
-
+
The network is abnormal
网络异常
@@ -788,13 +664,11 @@ Updated content:
SingleInstanceWindow
-
SingleInstance
-
I'm a SingleInstance window, and if I exist, I'll destroy the previous window and create a new one
我是一个SingleInstance模式的窗口,如果我存在,我会销毁之前的窗口,并创建一个新窗口
@@ -803,13 +677,11 @@ Updated content:
SingleTaskWindow
-
SingleTask
-
I'm a SingleTask mode window, and if I exist, I activate the window
我是一个SingleTask模式的窗口,如果我存在,我就激活窗口
@@ -818,79 +690,66 @@ Updated content:
StandardWindow
-
File
文件
-
New...
新建...
-
Open...
打开...
-
Save
保存
-
Save As...
另存为...
-
Quit
退出
-
Edit
编辑
-
Cut
剪切
-
Copy
复制
-
Paste
粘贴
-
Help
帮助
-
About
关于
-
I'm a Standard mode window, and every time I create a new window
我是一个Standard模式的窗口,每次我都会创建一个新的窗口
@@ -899,7 +758,6 @@ Updated content:
T_Acrylic
-
Acrylic
亚克力
@@ -908,25 +766,21 @@ Updated content:
T_Awesome
-
Awesome
图标
-
Please enter a keyword
请输入关键字
-
Search
搜索
-
You Copied
您复制
@@ -935,13 +789,11 @@ Updated content:
T_Badge
-
Badge
徽章
-
It usually appears in the upper right corner of the notification icon or avatar to display the number of messages that need to be processed
一般出现在通知图标或头像的右上角,用于显示需要处理的消息条数
@@ -950,7 +802,6 @@ Updated content:
T_BarChart
-
Bar Chart
条形图
@@ -959,13 +810,11 @@ Updated content:
T_BreadcrumbBar
-
BreadcurmbBar
面包屑
-
Reset sample
重置
@@ -974,7 +823,6 @@ Updated content:
T_BubbleChart
-
Bubble Chart
气泡图
@@ -983,19 +831,16 @@ Updated content:
T_Buttons
-
Buttons
按钮
-
Support the Tab key to switch focus, and the Space key to perform click events
支持Tab键切换焦点,空格键执行点击事件
-
Text Button
文本按钮
@@ -1008,164 +853,131 @@ Updated content:
-
-
-
-
-
-
-
-
Disabled
禁用
-
Standard Button
标准按钮
-
Click StandardButton
点击标准按钮
-
Filled Button
填充按钮
-
Click FilledButton
点击填充按钮
-
Toggle Button
开关按钮
-
Progress Button
进度按钮
-
Loading Button
加载按钮
-
Loading
正在加载
-
Click IconButton
点击图标按钮
-
IconOnly
-
Button.IconOnly
-
TextOnly
-
Button.TextOnly
-
TextBesideIcon
-
Button.TextBesideIcon
-
TextUnderIcon
-
Button.TextUnderIcon
-
DropDownButton
下拉按钮
-
Menu_1
-
Menu_2
-
Menu_3
-
Menu_4
-
Radio Button_1
-
Radio Button_2
-
Radio Button_3
@@ -1174,7 +986,6 @@ Updated content:
T_CalendarPicker
-
CalendarPicker
日历选择器
@@ -1183,37 +994,31 @@ Updated content:
T_Captcha
-
Captcha
验证码
-
Refresh
刷新
-
Ignore Case
忽略大小写
-
Please enter a verification code
请输入验证码
-
The verification code is correct
验证码错误
-
Error validation, please re-enter
错误验证,请重新输入
@@ -1222,13 +1027,11 @@ Updated content:
T_Carousel
-
Carousel
轮播图
-
Carousel map, support infinite carousel, infinite swipe, and components implemented with ListView
轮播图,支持无限轮播,无限滑动,用ListView实现的组件
@@ -1237,45 +1040,37 @@ Updated content:
T_CheckBox
-
CheckBox
复选框
-
A 2-state CheckBox
-
Right
右
-
Left
左
-
-
Disabled
禁用
-
A 3-state CheckBox
-
Three State
@@ -1284,13 +1079,11 @@ Updated content:
T_Clip
-
Clip
裁剪
-
Use with images (this component will have no effect under software rendering)
配合图片使用(software渲染下该组件将没有效果)
@@ -1299,61 +1092,51 @@ Updated content:
T_ColorPicker
-
ColorPicker
颜色选择器
-
Click to Select a Color - >
点击选中颜色 - >
-
Cancel
取消
-
OK
确定
-
Color Picker
颜色选择器
-
Edit Color
编辑颜色
-
Red
红色
-
Green
绿色
-
Blue
蓝色
-
Opacity
透明度
@@ -1362,7 +1145,6 @@ Updated content:
T_ComboBox
-
ComboBox
组合框
@@ -1371,44 +1153,36 @@ Updated content:
T_DatePicker
-
TimePicker
时间选择器
-
showYear=true
-
Year
- 年
+ 年
-
Month
- 月
+ 月
-
Day
- 日
+ 日
-
Cancel
- 取消
+ 取消
-
OK
- 确定
+ 确定
-
showYear=false
@@ -1417,13 +1191,11 @@ Updated content:
T_Dialog
-
Dialog
弹窗
-
Show Double Button Dialog
显示双按钮对话框
@@ -1431,25 +1203,18 @@ Updated content:
-
-
-
Friendly Reminder
友情提示
-
-
Are you sure you want to opt out?
您确定要退出吗?
-
-
Cancel
取消
@@ -1457,9 +1222,6 @@ Updated content:
-
-
-
Click the Cancel Button
单击取消按钮
@@ -1468,10 +1230,6 @@ Updated content:
-
-
-
-
OK
确定
@@ -1479,63 +1237,51 @@ Updated content:
-
-
-
Click the OK Button
单击确定按钮
-
Show Triple Button Dialog
显示三个按钮对话框
-
Minimize
最小化
-
Click Minimize
单击最小化
-
Custom Content Dialog
自定义对话框内容
-
Custom Content Dialog2
自定义对话框内容2
-
Data is loading, please wait...
正在加载,请稍等...
-
Unload
取消加载
-
Test the InfoBar level on top of the Popup
测试InfoBar层级在Popup之上
-
Line Chart
线型图
@@ -1544,25 +1290,21 @@ Updated content:
T_Expander
-
Expander
展开
-
Open a radio box
打开一个单选框
-
Open a sliding text box
打开一个滑动文本框
-
Permit me to observe: the late emperor was taken from us before he could finish his life`s work, the restoration of Han. Today, the empire is still divided in three, and our very survival is threatened. Yet still the officials at court and the soldiers throughout the realm remain loyal to you, your majesty. Because they remember the late emperor, all of them, and they wish to repay his kindness in service to you. This is the moment to extend your divine influence, to honour the memory of the late Emperor and strengthen the morale of your officers. It is not time to listen to bad advice, or close your ears to the suggestions of loyal men.
The court and the administration are as one. Both must be judged by one standard. Those who are loyal and good must get what they deserve, but so must the evil-doers who break the law. This will demonstrate the justice of your rule. There cannot be one law for the court and another for the administration.
Counselors and attendants like Guo Youzhi, Fei Yi, and Dong Yun are all reliable men, loyal of purpose and pure in motive. The late Emperor selected them for office so that they would serve you after his death.These are the men who should be consulted on all palace affairs. Xiang Chong has proved himself a fine general in battle, and the late Emperor believed in him. That is why the assembly has recommended him for overall command. It will keep the troops happy if he is consulted on all military matters.
@@ -1586,19 +1328,16 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_FlipView
-
FlipView
翻转视图
-
Horizontal FlipView
水平方向的翻转视图
-
Vertical FlipView
垂直方向的翻转视图
@@ -1607,25 +1346,21 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Home
-
FluentUI GitHub
FluentUI GitHub
-
The latest FluentUI controls and styles for your applications.
最新的 FluentUI 控件和样式
-
FluentUI Initalizr
FluentUI脚手架
-
FluentUI Initializr is a Tool that helps you create and customize Fluent UI projects with various options.
FluentUI 脚手架是一个快速创建项目工具,可帮助您创建和自定义具有各种选项的 Fluent UI 项目
@@ -1634,7 +1369,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Image
-
Image
图像
@@ -1644,7 +1378,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
-
The image failed to load, please reload
图片加载失败,请重新加载
@@ -1653,57 +1386,47 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_InfoBar
-
InfoBar
信息栏
-
Info
-
-
This is an InfoBar in the Info Style
这是一个Info样式的信息栏
-
Warning
-
This is an InfoBar in the Warning Style
这是一个Warning样式的信息栏
-
This is an InfoBar in the Error Style
这是一个Error样式的信息栏
-
This is an InfoBar in the Success Style
这是一个Success样式的信息栏
-
InfoBar that needs to be turned off manually
需要手动关闭的信息栏
-
Manual shutdown is supported
支持手动关闭
@@ -1716,7 +1439,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_LineChart
-
Line Chart
线型图
@@ -1726,56 +1448,42 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
-
-
Menu
菜单
-
-
File
文件
-
-
New...
新建...
-
-
Open...
打开...
-
-
Save
保存
-
-
Quit
退出
-
-
Search
搜索
@@ -1783,87 +1491,69 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
-
-
-
Disable
不可操作
-
Check
复选
-
-
Save As...
另存为...
-
-
Doc
DOC
-
-
PDF
PDF
-
Show Menu Popup
打开菜单弹框
-
MenuBar
菜单栏
-
Edit
编辑
-
Cut
剪切
-
Copy
复制
-
Paste
粘贴
-
Help
帮助
-
About
关于
@@ -1872,13 +1562,11 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_MultiWindow
-
MultiWindow
多窗口
-
<font color='red'>Standard</font> mode window,a new window is created every time
<font color='red'>Standard</font> 模式窗口,每次都会创建新窗口
@@ -1888,41 +1576,31 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
-
-
-
-
-
Create Window
创建窗口
-
<font color='red'>SingleTask</font> mode window,If a window exists, this activates the window
<font color='red'>SingleTask</font> 模式窗口,如果窗口存在,这激活该窗口
-
<font color='red'>SingleInstance</font> mode window,If the window exists, destroy the window and create a new window
<font color='red'>SingleInstance</font> 模式窗口,如果窗口存在,则销毁窗口,然后新建窗口
-
Create the window without carrying any parameters
创建一个窗口,不携带任何参数
-
Create a window with the parameter username: zhuzichu
创建一个窗口,并携带参数用户名:zhuzichu
-
Login Window Returned Password - >
登录窗口返回过来的密码 - >
@@ -1931,7 +1609,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Network
-
Network
网络
@@ -1940,7 +1617,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Pagination
-
Pagination
分页
@@ -1957,7 +1633,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_PieChart
-
Pie Chart
Doughnut and Pie Chart
饼图
@@ -1967,55 +1642,46 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Pivot
-
Pivot
轴转
-
All
所有
-
All emails go here.
所有电子邮件都在这里
-
Unread
未读
-
Unread emails go here.
未读电子邮件都在这里
-
Flagged
标记
-
Flagged emails go here.
标记电子邮件都在这里
-
Urgent
紧急
-
Urgent emails go here.
紧急电子邮件都在这里
@@ -2024,7 +1690,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_PolarAreaChart
-
Polar Area Chart
PolarArea Chart
极坐标区域图
@@ -2034,7 +1699,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Progress
-
Progress
进度条
@@ -2043,7 +1707,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_QRCode
-
QRCode
二维码
@@ -2052,7 +1715,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_RadarChart
-
Radar Chart
雷达图
@@ -2061,45 +1723,37 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_RadioButton
-
RadioButton
单选按钮
-
Right
右
-
Left
左
-
-
Disabled
禁用
-
Radio Button_1
-
Radio Button_2
-
Radio Button_3
@@ -2108,7 +1762,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_RatingControl
-
RatingControl
评级控制
@@ -2117,7 +1770,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Rectangle
-
Rectangle
矩形
@@ -2126,7 +1778,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_ScatterChart
-
Scatter Chart
散点图
@@ -2135,115 +1786,96 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Settings
-
Settings
设置
-
Current Version
当前版本
-
Check for Updates
检查更新
-
Use System AppBar
使用系统应用栏
-
Fits AppBar Windows
沉浸式应用栏
-
Friendly Reminder
友情提示
-
This action requires a restart of the program to take effect, is it restarted?
此操作需要重启程序才能生效,是否重新启动?
-
Cancel
取消
-
OK
确定
-
Dark Mode
夜间模式
-
System
跟随系统
-
Light
浅色
-
Dark
深色
-
Navigation View Display Mode
导航视图
-
Open
开放
-
Compact
紧凑
-
Minimal
极简
-
Auto
自动
-
Language
语言
@@ -2252,7 +1884,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_ShortcutPicker
-
ShortcutPicker
快捷键选择器
@@ -2281,7 +1912,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Slider
-
Slider
滑块
@@ -2290,7 +1920,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_SplitLayout
-
SplitLayout
拆分布局
@@ -2299,7 +1928,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_StaggeredLayout
-
StaggeredLayout
瀑布流布局
@@ -2308,31 +1936,26 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_StatusLayout
-
StatusLayout
状态布局
-
Loading...
正在加载...
-
Empty
空空如也
-
The page went wrong...
页面出错了...
-
Reload
重新加载
@@ -2341,13 +1964,11 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_TabView
-
TabView
选项卡
-
Document
文档
@@ -2356,123 +1977,102 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_TableView
-
TableView
表格
-
Modify the column name
修改列名
-
Cancel
取消
-
OK
确定
-
Search
搜索
-
-
Name
名称
-
Delete
删除
-
Edit
编辑
-
Select All
全选
-
Age
年龄
-
Clear All
清除所有
-
Avatar
头像
-
Address
地址
-
Nickname
昵称
-
Long String
长字符串
-
Options
操作
-
<Previous
<上一页
-
Next>
下一页>
-
Delete Selection
删除选中
-
Add a row of Data
添加一行数据
@@ -2481,13 +2081,11 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Text
-
Text
文本
-
This is a text that can be copied
这是一个可以复制的文本
@@ -2496,13 +2094,11 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_TextBox
-
TextBox
文本框
-
Single-line Input Box
单行输入框
@@ -2512,29 +2108,21 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
-
-
-
-
-
Disabled
禁用
-
Please enter your password
请输入您的密码
-
Multi-line Input Box
多行输入框
-
AutoSuggestBox
自动建议框
@@ -2543,37 +2131,31 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Theme
-
Theme
主题
-
Theme colors
主题颜色
-
Customize the Theme Color
自定义主题颜色
-
Dark Mode
夜间模式
-
Native Text
Native文本
-
Open Animation
开启动画
@@ -2582,67 +2164,52 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_TimePicker
-
TimePicker
时间选择器
-
hourFormat=FluTimePickerType.H
-
-
AM
上午
-
-
PM
下午
-
-
Hour
时
-
-
Minute
分
-
-
Cancel
取消
-
-
OK
确定
-
hourFormat=FluTimePickerType.HH
@@ -2651,19 +2218,16 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Timeline
-
Timeline
时间轴
-
Append
追加
-
clear
清空
@@ -2672,25 +2236,21 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_ToggleSwitch
-
ToggleSwitch
切换开关
-
Right
右
-
Left
左
-
Disabled
禁用
@@ -2699,45 +2259,37 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Tooltip
-
Tooltip
工具提示
-
Hover over Tultip and it pops up
鼠标悬停不动,弹出Tooltip
-
Text properties of FluIconButton support the Tooltip pop-up window by default
FluIconButton的text属性自带Tooltip效果
-
-
Delete
删除
-
Click IconButton
点击图标按钮
-
Add a Tooltip pop-up to a Button
给一个Button添加Tooltip效果
-
Click Button
点击按钮
@@ -2758,13 +2310,11 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
-
Upload File
上传文件
-
Put your files here.
把你的文件放在这里
@@ -2772,53 +2322,42 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
-
-
-
Save
保存
-
Save your changes.
保存更改
-
Other Actions
其他操作
-
Click to see other actions.
点击查看其他操作
-
Begin Tour
开始游览
-
-
Upload
上传
-
More
更多
-
Tour
游览
@@ -2827,19 +2366,16 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_TreeView
-
TreeView
树
-
Total %1 data, %2 data currently displayed
共计%1条数据,当前显示的%2条数据
-
A total of %1 data items are selected
共计选中%1条数据
@@ -2848,7 +2384,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Typography
-
Typography
字体
@@ -2857,7 +2392,6 @@ My only desire is to be permitted to drive out the traitors and restore the Han.
T_Watermark
-
Watermark
水印
diff --git a/example/qml-Qt6/App.qml b/example/qml-Qt6/App.qml
deleted file mode 100644
index 93f293fb..00000000
--- a/example/qml-Qt6/App.qml
+++ /dev/null
@@ -1,59 +0,0 @@
-import QtQuick
-import QtQuick.Window
-import QtQuick.Controls
-import QtQuick.Layouts
-import FluentUI
-
-Item {
- id: app
-
- Connections{
- target: FluTheme
- function onDarkModeChanged(){
- SettingsHelper.saveDarkMode(FluTheme.darkMode)
- }
- }
-
- Connections{
- target: FluApp
- function onUseSystemAppBarChanged(){
- SettingsHelper.saveUseSystemAppBar(FluApp.useSystemAppBar)
- }
- }
-
- Connections{
- target: TranslateHelper
- function onCurrentChanged(){
- SettingsHelper.saveLanguage(TranslateHelper.current)
- }
- }
-
- Component.onCompleted: {
- FluNetwork.openLog = false
- FluNetwork.setInterceptor(function(param){
- param.addHeader("Token","000000000000000000000")
- })
- FluApp.init(app,Qt.locale(TranslateHelper.current))
- FluApp.windowIcon = "qrc:/example/res/image/favicon.ico"
- FluApp.useSystemAppBar = SettingsHelper.getUseSystemAppBar()
- FluTheme.darkMode = SettingsHelper.getDarkMode()
- FluTheme.enableAnimation = true
- FluApp.routes = {
- "/":"qrc:/example/qml/window/MainWindow.qml",
- "/about":"qrc:/example/qml/window/AboutWindow.qml",
- "/login":"qrc:/example/qml/window/LoginWindow.qml",
- "/hotload":"qrc:/example/qml/window/HotloadWindow.qml",
- "/crash":"qrc:/example/qml/window/CrashWindow.qml",
- "/singleTaskWindow":"qrc:/example/qml/window/SingleTaskWindow.qml",
- "/standardWindow":"qrc:/example/qml/window/StandardWindow.qml",
- "/singleInstanceWindow":"qrc:/example/qml/window/SingleInstanceWindow.qml",
- "/pageWindow":"qrc:/example/qml/window/PageWindow.qml"
- }
- var args = Qt.application.arguments
- if(args.length>=2 && args[1].startsWith("-crashed=")){
- FluApp.navigate("/crash",{crashFilePath:args[1].replace("-crashed=","")})
- }else{
- FluApp.navigate("/")
- }
- }
-}
diff --git a/example/qml-Qt6/chart/T_BarChart.qml b/example/qml-Qt6/chart/T_BarChart.qml
deleted file mode 100644
index 859724c0..00000000
--- a/example/qml-Qt6/chart/T_BarChart.qml
+++ /dev/null
@@ -1,132 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Bar Chart")
-
- FluArea{
- width: 500
- height: 370
- paddings: 10
- Layout.topMargin: 20
- FluChart{
- anchors.fill: parent
- chartType: 'bar'
- chartData: { return {
- labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
- datasets: [{
- label: 'My First Dataset',
- data: [65, 59, 80, 81, 56, 55, 40],
- backgroundColor: [
- 'rgba(255, 99, 132, 0.2)',
- 'rgba(255, 159, 64, 0.2)',
- 'rgba(255, 205, 86, 0.2)',
- 'rgba(75, 192, 192, 0.2)',
- 'rgba(54, 162, 235, 0.2)',
- 'rgba(153, 102, 255, 0.2)',
- 'rgba(201, 203, 207, 0.2)'
- ],
- borderColor: [
- 'rgb(255, 99, 132)',
- 'rgb(255, 159, 64)',
- 'rgb(255, 205, 86)',
- 'rgb(75, 192, 192)',
- 'rgb(54, 162, 235)',
- 'rgb(153, 102, 255)',
- 'rgb(201, 203, 207)'
- ],
- borderWidth: 1
- }]
- }
- }
-
- chartOptions: { return {
- maintainAspectRatio: false,
- title: {
- display: true,
- text: 'Chart.js Bar Chart - Stacked'
- },
- tooltips: {
- mode: 'index',
- intersect: false
- },
- responsive: true,
- scales: {
- xAxes: [{
- stacked: true,
- }],
- yAxes: [{
- stacked: true
- }]
- }
- }
- }
- }
- }
-
- FluArea{
- width: 500
- height: 370
- paddings: 10
- Layout.topMargin: 20
- FluChart{
- anchors.fill: parent
- chartType: 'horizontalBar'
- chartData: { return {
- labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
- datasets: [{
- label: 'My First Dataset',
- data: [65, 59, 80, 81, 56, 55, 40],
- backgroundColor: [
- 'rgba(255, 99, 132, 0.2)',
- 'rgba(255, 159, 64, 0.2)',
- 'rgba(255, 205, 86, 0.2)',
- 'rgba(75, 192, 192, 0.2)',
- 'rgba(54, 162, 235, 0.2)',
- 'rgba(153, 102, 255, 0.2)',
- 'rgba(201, 203, 207, 0.2)'
- ],
- borderColor: [
- 'rgb(255, 99, 132)',
- 'rgb(255, 159, 64)',
- 'rgb(255, 205, 86)',
- 'rgb(75, 192, 192)',
- 'rgb(54, 162, 235)',
- 'rgb(153, 102, 255)',
- 'rgb(201, 203, 207)'
- ],
- borderWidth: 1
- }]
- }
- }
-
- chartOptions: { return {
- maintainAspectRatio: false,
- title: {
- display: true,
- text: 'Chart.js HorizontalBar Chart - Stacked'
- },
- tooltips: {
- mode: 'index',
- intersect: false
- },
- responsive: true,
- scales: {
- xAxes: [{
- stacked: true,
- }],
- yAxes: [{
- stacked: true
- }]
- }
- }
- }
- }
- }
-
-}
diff --git a/example/qml-Qt6/chart/T_BubbleChart.qml b/example/qml-Qt6/chart/T_BubbleChart.qml
deleted file mode 100644
index c5500f40..00000000
--- a/example/qml-Qt6/chart/T_BubbleChart.qml
+++ /dev/null
@@ -1,74 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Bubble Chart")
-
- function randomScalingFactor() {
- return Math.random().toFixed(1);
- }
-
- FluArea{
- height: 370
- width: 500
- paddings: 10
- Layout.topMargin: 20
- FluChart{
- anchors.fill: parent
- chartType: 'bubble'
- chartData: {
- return {
- datasets: [{
- label: 'First Dataset',
- data: [{
- x: 20,
- y: 30,
- r: 15
- }, {
- x: 12,
- y: 70,
- r: 20
- }, {
- x: 11,
- y: 28,
- r: 8
- }, {
- x: 9,
- y: 28,
- r: 10
- }, {
- x: 43,
- y: 7,
- r: 14
- }, {
- x: 22,
- y: 22,
- r: 12
- }, {
- x: 40,
- y: 10,
- r: 10
- }],
- backgroundColor: 'rgb(255, 99, 132)'
- }]
- }}
- chartOptions: {return {
- maintainAspectRatio: false,
- responsive: true,
- hoverMode: 'nearest',
- intersect: true,
- title: {
- display: true,
- text: 'Chart.js Bubble Chart - Multi Axis'
- }
- }
- }
- }
- }
-
-}
diff --git a/example/qml-Qt6/chart/T_LineChart.qml b/example/qml-Qt6/chart/T_LineChart.qml
deleted file mode 100644
index cf9e0340..00000000
--- a/example/qml-Qt6/chart/T_LineChart.qml
+++ /dev/null
@@ -1,45 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Line Chart")
-
- FluArea{
- width: 500
- height: 370
- paddings: 10
- Layout.topMargin: 20
- FluChart{
- anchors.fill: parent
- chartType: 'line'
- chartData: { return {
- labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
- datasets: [{
- label: 'My First Dataset',
- data: [65, 59, 80, 81, 56, 55, 40],
- fill: false,
- borderColor: 'rgb(75, 192, 192)',
- tension: 0.1
- }]
- }
- }
- chartOptions: { return {
- maintainAspectRatio: false,
- title: {
- display: true,
- text: 'Chart.js Line Chart - Stacked'
- },
- tooltips: {
- mode: 'index',
- intersect: false
- }
- }
- }
- }
- }
-}
diff --git a/example/qml-Qt6/chart/T_PieChart.qml b/example/qml-Qt6/chart/T_PieChart.qml
deleted file mode 100644
index 5d6b6345..00000000
--- a/example/qml-Qt6/chart/T_PieChart.qml
+++ /dev/null
@@ -1,93 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Pie Chart")
-
- FluArea{
- width: 500
- height: 370
- paddings: 10
- Layout.topMargin: 20
- FluChart{
- anchors.fill: parent
- chartType: "doughnut"
- chartData: { return {
- labels: [
- 'Red',
- 'Blue',
- 'Yellow'
- ],
- datasets: [{
- label: 'My First Dataset',
- data: [300, 50, 100],
- backgroundColor: [
- 'rgb(255, 99, 132)',
- 'rgb(54, 162, 235)',
- 'rgb(255, 205, 86)'
- ],
- hoverOffset: 4
- }]
- }
- }
- chartOptions: { return {
- maintainAspectRatio: false,
- title: {
- display: true,
- text: 'Chart.js Doughnut Chart - Stacked'
- },
- tooltips: {
- mode: 'index',
- intersect: false
- }
- }
- }
- }
- }
-
- FluArea{
- width: 500
- height: 370
- paddings: 10
- Layout.topMargin: 20
- FluChart{
- anchors.fill: parent
- chartType: "pie"
- chartData: { return {
- labels: [
- 'Red',
- 'Blue',
- 'Yellow'
- ],
- datasets: [{
- label: 'My First Dataset',
- data: [300, 50, 100],
- backgroundColor: [
- 'rgb(255, 99, 132)',
- 'rgb(54, 162, 235)',
- 'rgb(255, 205, 86)'
- ],
- hoverOffset: 4
- }]
- }
- }
- chartOptions: { return {
- maintainAspectRatio: false,
- title: {
- display: true,
- text: 'Chart.js Pie Chart - Stacked'
- },
- tooltips: {
- mode: 'index',
- intersect: false
- }
- }
- }
- }
- }
-}
diff --git a/example/qml-Qt6/chart/T_PolarAreaChart.qml b/example/qml-Qt6/chart/T_PolarAreaChart.qml
deleted file mode 100644
index 6e86529d..00000000
--- a/example/qml-Qt6/chart/T_PolarAreaChart.qml
+++ /dev/null
@@ -1,57 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Polar Area Chart")
-
- FluArea{
- width: 500
- height: 370
- paddings: 10
- Layout.topMargin: 20
- FluChart{
- anchors.fill: parent
- chartType: 'polarArea'
- chartData: { return {
- labels: [
- 'Red',
- 'Green',
- 'Yellow',
- 'Grey',
- 'Blue'
- ],
- datasets: [{
- label: 'My First Dataset',
- data: [11, 16, 7, 3, 14],
- backgroundColor: [
- 'rgb(255, 99, 132)',
- 'rgb(75, 192, 192)',
- 'rgb(255, 205, 86)',
- 'rgb(201, 203, 207)',
- 'rgb(54, 162, 235)'
- ]
- }]
- }
- }
-
- chartOptions: { return {
- maintainAspectRatio: false,
- title: {
- display: true,
- text: 'Chart.js PolarArea Chart - Stacked'
- },
- tooltips: {
- mode: 'index',
- intersect: false
- }
- }
- }
- }
- }
-
-}
diff --git a/example/qml-Qt6/chart/T_RadarChart.qml b/example/qml-Qt6/chart/T_RadarChart.qml
deleted file mode 100644
index 76b49925..00000000
--- a/example/qml-Qt6/chart/T_RadarChart.qml
+++ /dev/null
@@ -1,74 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Radar Chart")
-
- FluArea{
- width: 500
- height: 370
- paddings: 10
- Layout.topMargin: 20
- FluChart{
- anchors.fill: parent
- chartType: 'radar'
- chartData: { return {
- labels: [
- 'Eating',
- 'Drinking',
- 'Sleeping',
- 'Designing',
- 'Coding',
- 'Cycling',
- 'Running'
- ],
- datasets:
- [{
- label: 'My First Dataset',
- data: [65, 59, 90, 81, 56, 55, 40],
- fill: true,
- backgroundColor: 'rgba(255, 99, 132, 0.2)',
- borderColor: 'rgb(255, 99, 132)',
- pointBackgroundColor: 'rgb(255, 99, 132)',
- pointBorderColor: '#fff',
- pointHoverBackgroundColor: '#fff',
- pointHoverBorderColor: 'rgb(255, 99, 132)'
- }, {
- label: 'My Second Dataset',
- data: [28, 48, 40, 19, 96, 27, 100],
- fill: true,
- backgroundColor: 'rgba(54, 162, 235, 0.2)',
- borderColor: 'rgb(54, 162, 235)',
- pointBackgroundColor: 'rgb(54, 162, 235)',
- pointBorderColor: '#fff',
- pointHoverBackgroundColor: '#fff',
- pointHoverBorderColor: 'rgb(54, 162, 235)'
- }]
- }
- }
- chartOptions: { return {
- maintainAspectRatio: false,
- title: {
- display: true,
- text: 'Chart.js Radar Chart - Stacked'
- },
- tooltips: {
- mode: 'index',
- intersect: false
- },
- elements: {
- line: {
- borderWidth: 3
- }
- }
- }
- }
- }
- }
-
-}
diff --git a/example/qml-Qt6/chart/T_ScatterChart.qml b/example/qml-Qt6/chart/T_ScatterChart.qml
deleted file mode 100644
index 2e0778c6..00000000
--- a/example/qml-Qt6/chart/T_ScatterChart.qml
+++ /dev/null
@@ -1,123 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Scatter Chart")
-
- function randomScalingFactor() {
- return Math.random().toFixed(1);
- }
-
- FluArea{
- height: 370
- width: 500
- paddings: 10
- Layout.topMargin: 20
- FluChart{
- anchors.fill: parent
- chartType: 'scatter'
- chartData: {
- return {
- datasets: [{
- label: 'My First dataset',
- xAxisID: 'x-axis-1',
- yAxisID: 'y-axis-1',
- borderColor: '#ff5555',
- backgroundColor: 'rgba(255,192,192,0.3)',
- data: [{
- x: randomScalingFactor(),
- y: randomScalingFactor(),
- }, {
- x: randomScalingFactor(),
- y: randomScalingFactor(),
- }, {
- x: randomScalingFactor(),
- y: randomScalingFactor(),
- }, {
- x: randomScalingFactor(),
- y: randomScalingFactor(),
- }, {
- x: randomScalingFactor(),
- y: randomScalingFactor(),
- }, {
- x: randomScalingFactor(),
- y: randomScalingFactor(),
- }, {
- x: randomScalingFactor(),
- y: randomScalingFactor(),
- }]
- }, {
- label: 'My Second dataset',
- xAxisID: 'x-axis-1',
- yAxisID: 'y-axis-2',
- borderColor: '#5555ff',
- backgroundColor: 'rgba(192,192,255,0.3)',
- data: [{
- x: randomScalingFactor(),
- y: randomScalingFactor(),
- }, {
- x: randomScalingFactor(),
- y: randomScalingFactor(),
- }, {
- x: randomScalingFactor(),
- y: randomScalingFactor(),
- }, {
- x: randomScalingFactor(),
- y: randomScalingFactor(),
- }, {
- x: randomScalingFactor(),
- y: randomScalingFactor(),
- }, {
- x: randomScalingFactor(),
- y: randomScalingFactor(),
- }, {
- x: randomScalingFactor(),
- y: randomScalingFactor(),
- }]
- }]
- }}
- chartOptions: {return {
- maintainAspectRatio: false,
- responsive: true,
- hoverMode: 'nearest',
- intersect: true,
- title: {
- display: true,
- text: 'Chart.js Scatter Chart - Multi Axis'
- },
- scales: {
- xAxes: [{
- position: 'bottom',
- gridLines: {
- zeroLineColor: 'rgba(0,0,0,1)'
- }
- }],
- yAxes: [{
- type: 'linear', // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
- display: true,
- position: 'left',
- id: 'y-axis-1',
- }, {
- type: 'linear', // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
- display: true,
- position: 'right',
- reverse: true,
- id: 'y-axis-2',
-
- // grid line settings
- gridLines: {
- drawOnChartArea: false, // only want the grid lines for one axis to show up
- },
- }],
- }
- }
- }
- }
- }
-
-}
diff --git a/example/qml-Qt6/component/CodeExpander.qml b/example/qml-Qt6/component/CodeExpander.qml
deleted file mode 100644
index 52d0291d..00000000
--- a/example/qml-Qt6/component/CodeExpander.qml
+++ /dev/null
@@ -1,144 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-
-FluExpander{
-
- id:control
- property string code: ""
- headerText: qsTr("Source")
- contentHeight:content.height
- focus: false
-
- FluCopyableText{
- id:content
- width:parent.width
- text:highlightQmlCode(code)
- textFormat: FluMultilineTextBox.RichText
- padding: 10
- topPadding: 10
- leftPadding: 10
- rightPadding: 10
- bottomPadding: 10
- }
-
- FluIconButton{
- iconSource:FluentIcons.Copy
- anchors{
- right: parent.right
- top: parent.top
- rightMargin: 5
- topMargin: 5
- }
- onClicked:{
- FluTools.clipText(FluTools.html2PlantText(content.text))
- showSuccess(qsTr("The Copy is Successful"))
- }
- }
-
- function htmlEncode(e){
- var i,s;
- for(i in s={
- "&":/&/g,//""//":/"/g,"'":/'/g,
- "<":/":/>/g,"
":/\n/g,
- " ":/ /g," ":/\t/g
- })e=e.replace(s[i],i);
- return e;
- }
-
- function highlightQmlCode(code) {
- var qmlKeywords = [
- "FluTextButton",
- "FluAppBar",
- "FluAutoSuggestBox",
- "FluBadge",
- "FluButton",
- "FluCalendarPicker",
- "FluCalendarView",
- "FluCarousel",
- "FluCheckBox",
- "FluColorPicker",
- "FluColorView",
- "FluComboBox",
- "FluContentDialog",
- "FluContentPage",
- "FluDatePicker",
- "FluDivider",
- "FluDropDownButton",
- "FluExpander",
- "FluFilledButton",
- "FluFlipView",
- "FluFocusRectangle",
- "FluIcon",
- "FluIconButton",
- "FluInfoBar",
- "FluMediaPlayer",
- "FluMenu",
- "FluMenuItem",
- "FluMultilineTextBox",
- "FluNavigationView",
- "FluObject",
- "FluPaneItem",
- "FluPaneItemExpander",
- "FluPaneItemHeader",
- "FluPaneItemSeparator",
- "FluPivot",
- "FluPivotItem",
- "FluProgressBar",
- "FluProgressRing",
- "FluRadioButton",
- "FluRectangle",
- "FluScrollablePage",
- "FluScrollBar",
- "FluShadow",
- "FluSlider",
- "FluTabView",
- "FluText",
- "FluTextArea",
- "FluTextBox",
- "FluTextBoxBackground",
- "FluTextBoxMenu",
- "FluTextButton",
- "FluTextFiled",
- "FluTimePicker",
- "FluToggleSwitch",
- "FluTooltip",
- "FluTreeView",
- "FluWindow",
- "FluWindowResize",
- "FluToggleButton",
- "FluTableView",
- "FluColors",
- "FluTheme",
- "FluStatusLayout",
- "FluRatingControl",
- "FluPasswordBox",
- "FluBreadcrumbBar",
- "FluCopyableText",
- "FluAcrylic",
- "FluRemoteLoader",
- "FluMenuBar",
- "FluPagination",
- "FluRadioButtons",
- "FluImage",
- "FluSpinBox",
- "FluWatermark",
- "FluTour",
- "FluQRCode",
- "FluTimeline",
- "FluChart",
- "FluRangeSlider",
- "FluStaggeredLayout",
- "FluProgressButton",
- "FluLoadingButton",
- "FluClip",
- "FluNetwork",
- "FluShortcutPicker"
- ];
- code = code.replace(/\n/g, "
");
- code = code.replace(/ /g, " ");
- return code.replace(RegExp("\\b(" + qmlKeywords.join("|") + ")\\b", "g"), "$1");
- }
-}
diff --git a/example/qml-Qt6/global/ItemsFooter.qml b/example/qml-Qt6/global/ItemsFooter.qml
deleted file mode 100644
index 85d466f8..00000000
--- a/example/qml-Qt6/global/ItemsFooter.qml
+++ /dev/null
@@ -1,33 +0,0 @@
-pragma Singleton
-
-import QtQuick
-import FluentUI
-
-FluObject{
-
- property var navigationView
- property var paneItemMenu
-
- id:footer_items
-
- FluPaneItemSeparator{}
-
- FluPaneItem{
- title:qsTr("About")
- icon:FluentIcons.Contact
- onTapListener:function(){
- FluApp.navigate("/about")
- }
- }
-
- FluPaneItem{
- title:qsTr("Settings")
- menuDelegate: paneItemMenu
- icon:FluentIcons.Settings
- url:"qrc:/example/qml/page/T_Settings.qml"
- onTap:{
- navigationView.push(url)
- }
- }
-
-}
diff --git a/example/qml-Qt6/global/ItemsOriginal.qml b/example/qml-Qt6/global/ItemsOriginal.qml
deleted file mode 100644
index 13137a8b..00000000
--- a/example/qml-Qt6/global/ItemsOriginal.qml
+++ /dev/null
@@ -1,532 +0,0 @@
-pragma Singleton
-
-import QtQuick
-import FluentUI
-
-FluObject{
-
- property var navigationView
- property var paneItemMenu
-
- function rename(item, newName){
- if(newName && newName.trim().length>0){
- item.title = newName;
- }
- }
-
- FluPaneItem{
- id:item_home
- count: 9
- title: qsTr("Home")
- menuDelegate: paneItemMenu
- infoBadge: FluBadge{
- count: item_home.count
- }
- icon: FluentIcons.Home
- url: "qrc:/example/qml/page/T_Home.qml"
- onTap: {
- if(navigationView.getCurrentUrl()){
- item_home.count = 0
- }
- navigationView.push(url)
- }
- }
-
- FluPaneItemExpander{
- title: qsTr("PaneItemExpander Disabled")
- iconVisible: false
- disabled: true
- }
-
- FluPaneItemExpander{
- id: item_expander_basic_input
- title: qsTr("Basic Input")
- icon: FluentIcons.CheckboxComposite
- FluPaneItem{
- id: item_buttons
- count: 99
- infoBadge: FluBadge{
- count: item_buttons.count
- }
- title: qsTr("Buttons")
- menuDelegate: paneItemMenu
- extra: ({image:"qrc:/example/res/image/control/Button.png",recentlyUpdated:true,desc:qsTr("A control that responds to user input and raisesa Click event.")})
- url: "qrc:/example/qml/page/T_Buttons.qml"
- onTap: {
- item_buttons.count = 0
- navigationView.push(url)
- }
- }
- FluPaneItem{
- id: item_text
- title: qsTr("Text")
- menuDelegate: paneItemMenu
- count: 5
- infoBadge: FluBadge{
- count: item_text.count
- color: Qt.rgba(82/255,196/255,26/255,1)
- }
- url: "qrc:/example/qml/page/T_Text.qml"
- onTap: {
- item_text.count = 0
- navigationView.push(url)
- }
- }
- FluPaneItem{
- title: qsTr("Image")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Image.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Slider")
- menuDelegate: paneItemMenu
- extra: ({image:"qrc:/example/res/image/control/Slider.png",recentlyUpdated:true,desc:qsTr("A control that lets the user select from a rangeof values by moving a Thumb control along atrack.")})
- url: "qrc:/example/qml/page/T_Slider.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("CheckBox")
- menuDelegate: paneItemMenu
- extra: ({image:"qrc:/example/res/image/control/Checkbox.png",recentlyUpdated:true,desc:qsTr("A control that a user can select or clear.")})
- url: "qrc:/example/qml/page/T_CheckBox.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("RadioButton")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_RadioButton.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("ToggleSwitch")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_ToggleSwitch.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("PaneItem Disabled")
- disabled: true
- icon: FluentIcons.Error
- }
- }
-
- FluPaneItemExpander{
- title: qsTr("Form")
- icon: FluentIcons.GridView
- FluPaneItem {
- title: qsTr("TextBox")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_TextBox.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("TimePicker")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_TimePicker.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("DatePicker")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_DatePicker.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("CalendarPicker")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_CalendarPicker.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("ColorPicker")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_ColorPicker.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("ShortcutPicker")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_ShortcutPicker.qml"
- onTap: { navigationView.push(url) }
- }
- }
-
- FluPaneItemExpander{
- title: qsTr("Surface")
- icon: FluentIcons.SurfaceHub
- FluPaneItem{
- title: qsTr("InfoBar")
- menuDelegate: paneItemMenu
- extra: ({image:"qrc:/example/res/image/control/InfoBar.png",recentlyUpdated:true,desc:qsTr("An inline message to display app-wide statuschange information.")})
- url: "qrc:/example/qml/page/T_InfoBar.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Progress")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Progress.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("RatingControl")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_RatingControl.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Badge")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Badge.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Rectangle")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Rectangle.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Clip")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Clip.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Carousel")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Carousel.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Expander")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Expander.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Watermark")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Watermark.qml"
- onTap: { navigationView.push(url) }
- }
- }
-
- FluPaneItemExpander{
- title: qsTr("Layout")
- icon: FluentIcons.DockLeft
- FluPaneItem {
- title: qsTr("StaggeredLayout")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_StaggeredLayout.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("SplitLayout")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_SplitLayout.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("StatusLayout")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_StatusLayout.qml"
- onTap: { navigationView.push(url) }
- }
- }
-
- FluPaneItemExpander{
- title: qsTr("Popus")
- icon: FluentIcons.ButtonMenu
- FluPaneItem{
- title: qsTr("Dialog")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Dialog.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- id: item_combobox
- title: qsTr("ComboBox")
- menuDelegate: paneItemMenu
- count: 9
- infoBadge:FluBadge{
- count: item_combobox.count
- color: Qt.rgba(250/255,173/255,20/255,1)
- }
- url: "qrc:/example/qml/page/T_ComboBox.qml"
- onTap: {
- item_combobox.count = 0
- navigationView.push("qrc:/example/qml/page/T_ComboBox.qml")
- }
- }
- FluPaneItem{
- title: qsTr("Tooltip")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Tooltip.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Menu")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Menu.qml"
- onTap: { navigationView.push(url) }
- }
- }
-
- FluPaneItemExpander{
- title: qsTr("Navigation")
- icon: FluentIcons.AllApps
- FluPaneItem{
- title: qsTr("Pivot")
- menuDelegate: paneItemMenu
- extra: ({image:"qrc:/example/res/image/control/Pivot.png",order:3,recentlyAdded:true,desc:qsTr("Presents information from different sources in a tabbed view.")})
- url: "qrc:/example/qml/page/T_Pivot.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("BreadcrumbBar")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_BreadcrumbBar.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("TabView")
- menuDelegate: paneItemMenu
- extra: ({image:"qrc:/example/res/image/control/TabView.png",order:1,recentlyAdded:true,desc:qsTr("A control that displays a collection of tabs thatcan be used to display several documents.")})
- url: "qrc:/example/qml/page/T_TabView.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("TreeView")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_TreeView.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("TableView")
- menuDelegate: paneItemMenu
- extra: ({image:"qrc:/example/res/image/control/DataGrid.png",order:4,recentlyAdded:true,desc:qsTr("The TableView control provides a flexible way to display a collection of data in rows and columns")})
- url: "qrc:/example/qml/page/T_TableView.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Pagination")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Pagination.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("MultiWindow")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_MultiWindow.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("FlipView")
- menuDelegate: paneItemMenu
- extra: ({image:"qrc:/example/res/image/control/FlipView.png",order:2,recentlyAdded:true,desc:qsTr("Presents a collection of items that the user canflip through, one item at a time.")})
- url: "qrc:/example/qml/page/T_FlipView.qml"
- onTap: { navigationView.push(url) }
- }
- }
-
- FluPaneItemExpander{
- title: qsTr("Theming")
- icon:FluentIcons.Brightness
- FluPaneItem{
- title: qsTr("Acrylic")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Acrylic.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Theme")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Theme.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Typography")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Typography.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Awesome")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Awesome.qml"
- onTap: { navigationView.push(url) }
- }
- }
-
- FluPaneItemExpander{
- title: qsTr("Chart")
- icon: FluentIcons.AreaChart
- FluPaneItem{
- title: qsTr("Bar Chart")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/chart/T_BarChart.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Line Chart")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/chart/T_LineChart.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Pie Chart")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/chart/T_PieChart.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Polar Area Chart")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/chart/T_PolarAreaChart.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Bubble Chart")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/chart/T_BubbleChart.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Scatter Chart")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/chart/T_ScatterChart.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Radar Chart")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/chart/T_RadarChart.qml"
- onTap: { navigationView.push(url) }
- }
- }
-
- FluPaneItemSeparator{
- spacing:10
- size:1
- }
-
- FluPaneItemExpander{
- title: qsTr("Other")
- icon: FluentIcons.Shop
- FluPaneItem{
- title: qsTr("QRCode")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_QRCode.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Tour")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Tour.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Timeline")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Timeline.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Captcha")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Captcha.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Network")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_Network.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- id: item_other
- title: qsTr("Remote Loader")
- menuDelegate: paneItemMenu
- count: 99
- infoBadge:FluBadge{
- count: item_other.count
- color: Qt.rgba(82/255,196/255,26/255,1)
- }
- url: "qrc:/example/qml/page/T_RemoteLoader.qml"
- onTap: {
- item_other.count = 0
- navigationView.push("qrc:/example/qml/page/T_RemoteLoader.qml")
- }
- }
- FluPaneItem{
- title: qsTr("Hot Loader")
- onTapListener: function(){
- FluApp.navigate("/hotload")
- }
- }
- FluPaneItem{
- title: qsTr("3D")
- menuDelegate: paneItemMenu
- url: "qrc:/example/qml/page/T_3D.qml"
- onTap: { navigationView.push(url) }
- }
- FluPaneItem{
- title: qsTr("Test Crash")
- visible: FluTools.isWin()
- onTapListener: function(){
- AppInfo.testCrash()
- }
- }
- }
-
- function getRecentlyAddedData(){
- var arr = []
- var items = navigationView.getItems();
- for(var i=0;i ${item.title}`,key:item.key})
- }
- else
- arr.push({title:item.title,key:item.key})
- }
- }
- return arr
- }
-
- function startPageByItem(data){
- navigationView.startPageByItem(data)
- }
-
-}
diff --git a/example/qml-Qt6/global/qmldir b/example/qml-Qt6/global/qmldir
deleted file mode 100644
index eb6bfeaf..00000000
--- a/example/qml-Qt6/global/qmldir
+++ /dev/null
@@ -1,2 +0,0 @@
-singleton ItemsOriginal 1.0 ItemsOriginal.qml
-singleton ItemsFooter 1.0 ItemsFooter.qml
diff --git a/example/qml-Qt6/page/T_3D.qml b/example/qml-Qt6/page/T_3D.qml
deleted file mode 100644
index d4b38536..00000000
--- a/example/qml-Qt6/page/T_3D.qml
+++ /dev/null
@@ -1,117 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Controls
-import QtQuick.Window
-import FluentUI
-import Qt3D.Core
-import Qt3D.Render
-import Qt3D.Input
-import Qt3D.Extras
-import QtQuick.Scene3D
-import QtQuick.Dialogs
-import Qt.labs.platform
-import "../component"
-
-FluContentPage{
-
- id:root
- title:"3D"
-
- Scene3D{
- id:scene_3d
- anchors.fill: parent
- focus: true
- aspects: ["input", "logic"]
- cameraAspectRatioMode: Scene3D.AutomaticAspectRatio
- Entity {
- Camera {
- id: camera
- projectionType: CameraLens.PerspectiveProjection
- fieldOfView: 22.5
- aspectRatio: scene_3d.width / scene_3d.height
- nearPlane: 1
- farPlane: 1000.0
- viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
- upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
- position: Qt.vector3d( 0.0, 0.0, 15.0 )
- }
- FirstPersonCameraController {
- linearSpeed: 100
- lookSpeed: 50
- camera: camera
- }
- components: [
- RenderSettings{
- activeFrameGraph: ForwardRenderer{
- clearColor: Qt.rgba(0,0,0,0);
- camera: camera
- }
- },
- InputSettings{}
- ]
- Mesh {
- id: mesh
- source: "https://zhu-zichu.gitee.io/test.obj"
- }
- PhongMaterial {
- id: material
- ambient: color_picker.current
- }
- Transform{
- id:transform
- scale: 1.0
- translation: Qt.vector3d(0, 0, 0)
- rotation: fromEulerAngles(0, 0, 0)
- property real hAngle:0.0
- NumberAnimation on hAngle{
- from:0
- to:360.0
- duration: 5000
- loops: Animation.Infinite
- }
- matrix:{
- var m=Qt.matrix4x4();
- m.rotate(hAngle,Qt.vector3d(0,1,0));
- m.translate(Qt.vector3d(0,0,0));
- return m;
- }
- }
- Entity {
- id: entity
- components: [mesh, material,transform]
- }
- }
- }
- ColumnLayout{
- RowLayout{
- spacing: 10
- Layout.topMargin: 20
- FluText{
- text:"tintColor:"
- Layout.alignment: Qt.AlignVCenter
- }
- FluColorPicker{
- id:color_picker
- current: "gray"
- }
- }
-
- FluButton{
- text:"选择obj资源"
- onClicked: {
- file_dialog.open()
- }
- }
- }
-
- FileDialog {
- id: file_dialog
- nameFilters: ["Obj files (*.obj)"]
- folder: StandardPaths.writableLocation(StandardPaths.DocumentsLocation)
- onAccepted: {
- var fileUrl = file_dialog.currentFile
- mesh.source = fileUrl
- }
- }
-
-}
diff --git a/example/qml-Qt6/page/T_Acrylic.qml b/example/qml-Qt6/page/T_Acrylic.qml
deleted file mode 100644
index eb1e0801..00000000
--- a/example/qml-Qt6/page/T_Acrylic.qml
+++ /dev/null
@@ -1,115 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Acrylic")
-
- RowLayout{
- spacing: 10
- Layout.topMargin: 20
- FluText{
- text:"tintColor:"
- Layout.alignment: Qt.AlignVCenter
- }
- FluColorPicker{
- id:color_picker
- }
- }
- RowLayout{
- spacing: 10
- FluText{
- text:"tintOpacity:"
- Layout.alignment: Qt.AlignVCenter
- }
- FluSlider{
- id:slider_tint_opacity
- value: 65
- }
- }
- RowLayout{
- spacing: 10
- FluText{
- text:"blurRadius:"
- Layout.alignment: Qt.AlignVCenter
- }
- FluSlider{
- id:slider_blur_radius
- value: 32
- }
- }
- FluArea{
- Layout.fillWidth: true
- height: 1200/4+20
- paddings: 10
- Layout.topMargin: 10
- FluClip{
- width: 1920/4
- height: 1200/4
- radius:[8,8,8,8]
- Image {
- id:image
- asynchronous: true
- source: "qrc:/example/res/image/bg_scenic.png"
- anchors.fill: parent
- sourceSize: Qt.size(2*width,2*height)
- }
- FluAcrylic {
- id:acrylic
- target: image
- width: 200
- height: 200
- tintOpacity: slider_tint_opacity.value/100
- tintColor: color_picker.current
- blurRadius: slider_blur_radius.value
- x:(image.width-width)/2
- y:(image.height-height)/2
- FluText {
- anchors.centerIn: parent
- text: "Acrylic"
- color: "#FFFFFF"
- font.bold: true
- }
- MouseArea {
- property point clickPos: Qt.point(0,0)
- id:drag_area
- preventStealing: true
- anchors.fill: parent
- onPressed: (mouse)=>{
- clickPos = Qt.point(mouse.x, mouse.y)
- }
- onPositionChanged: (mouse)=>{
- var delta = Qt.point(mouse.x - clickPos.x,mouse.y - clickPos.y)
- acrylic.x = acrylic.x + delta.x
- acrylic.y = acrylic.y + delta.y
- }
- }
- }
- Layout.topMargin: 20
- }
-
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'Image{
- id:image
- width: 800
- height: 600
- source: "qrc:/example/res/image/image_huoyin.webp"
- radius: 8
- }
- FluAcrylic{
- target:image
- width: 100
- height: 100
- anchors.centerIn: parent
- }
-}'
- }
-
-}
diff --git a/example/qml-Qt6/page/T_Awesome.qml b/example/qml-Qt6/page/T_Awesome.qml
deleted file mode 100644
index d8e660f5..00000000
--- a/example/qml-Qt6/page/T_Awesome.qml
+++ /dev/null
@@ -1,71 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Window
-import FluentUI
-
-FluContentPage {
-
- title: qsTr("Awesome")
-
- FluTextBox{
- id:text_box
- placeholderText: qsTr("Please enter a keyword")
- anchors{
- topMargin: 20
- top:parent.top
- }
- }
-
- FluFilledButton{
- text: qsTr("Search")
- anchors{
- left: text_box.right
- verticalCenter: text_box.verticalCenter
- leftMargin: 14
- }
- onClicked: {
- grid_view.model = FluTheme.awesomeList(text_box.text)
- }
- }
- GridView{
- id:grid_view
- cellWidth: 80
- cellHeight: 80
- clip: true
- boundsBehavior: GridView.StopAtBounds
- model:FluTheme.awesomeList()
- ScrollBar.vertical: FluScrollBar {}
- anchors{
- topMargin: 10
- top:text_box.bottom
- left: parent.left
- right: parent.right
- bottom: parent.bottom
- }
- delegate: Item {
- width: 68
- height: 80
- FluIconButton{
- id:item_icon
- iconSource:modelData.icon
- anchors.horizontalCenter: parent.horizontalCenter
- onClicked: {
- var text ="FluentIcons."+modelData.name;
- FluTools.clipText(text)
- showSuccess(qsTr("You Copied ")+text)
- }
- }
- FluText {
- id:item_name
- font.pixelSize: 10
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.top: item_icon.bottom
- width:parent.width
- wrapMode: Text.WrapAnywhere
- text: modelData.name
- horizontalAlignment: Text.AlignHCenter
- }
- }
- }
-}
diff --git a/example/qml-Qt6/page/T_Badge.qml b/example/qml-Qt6/page/T_Badge.qml
deleted file mode 100644
index 4a536a3c..00000000
--- a/example/qml-Qt6/page/T_Badge.qml
+++ /dev/null
@@ -1,131 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Badge")
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 120
- paddings: 10
-
- Column{
- spacing: 15
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- right: parent.right
- }
- FluText{
- wrapMode: Text.WrapAnywhere
- width: parent.width
- text: qsTr("It usually appears in the upper right corner of the notification icon or avatar to display the number of messages that need to be processed")
- }
- Row{
- spacing: 20
- Rectangle{
- width: 40
- height: 40
- radius: 8
- color: Qt.rgba(191/255,191/255,191/255,1)
- FluBadge{
- topRight: true
- showZero: true
- count:0
- }
- }
-
- Rectangle{
- width: 40
- height: 40
- radius: 8
- color: Qt.rgba(191/255,191/255,191/255,1)
- FluBadge{
- topRight: true
- showZero: true
- count:5
- }
- }
- Rectangle{
- width: 40
- height: 40
- radius: 8
- color: Qt.rgba(191/255,191/255,191/255,1)
- FluBadge{
- topRight: true
- showZero: true
- count:50
- }
- }
- Rectangle{
- width: 40
- height: 40
- radius: 8
- color: Qt.rgba(191/255,191/255,191/255,1)
- FluBadge{
- topRight: true
- showZero: true
- count:100
- }
- }
- Rectangle{
- width: 40
- height: 40
- radius: 8
- color: Qt.rgba(191/255,191/255,191/255,1)
- FluBadge{
- topRight: true
- showZero: true
- isDot:true
- }
- }
- Rectangle{
- width: 40
- height: 40
- radius: 8
- color: Qt.rgba(191/255,191/255,191/255,1)
- FluBadge{
- topRight: true
- showZero: true
- count:99
- color: Qt.rgba(250/255,173/255,20/255,1)
- }
- }
- Rectangle{
- width: 40
- height: 40
- radius: 8
- color: Qt.rgba(191/255,191/255,191/255,1)
- FluBadge{
- topRight: true
- showZero: true
- count:99
- color: Qt.rgba(82/255,196/255,26/255,1)
- }
- }
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'Rectangle{
- width: 40
- height: 40
- radius: 8
- color: Qt.rgba(191/255,191/255,191/255,1)
- FluBadge{
- count: 100
- isDot: false
- color: Qt.rgba(82/255,196/255,26/255,1)
- }
-}'
- }
-
-}
diff --git a/example/qml-Qt6/page/T_BreadcrumbBar.qml b/example/qml-Qt6/page/T_BreadcrumbBar.qml
deleted file mode 100644
index 51cc829d..00000000
--- a/example/qml-Qt6/page/T_BreadcrumbBar.qml
+++ /dev/null
@@ -1,93 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Window
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("BreadcurmbBar")
-
- Component.onCompleted: {
- var items = []
- for(var i=0;i<10;i++){
- items.push({title:"Item_"+(i+1)})
- }
- breadcrumb_1.items = items
- breadcrumb_2.items = items
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 68
- paddings: 10
- Layout.topMargin: 20
-
- FluBreadcrumbBar{
- id:breadcrumb_1
- width:parent.width
- anchors.verticalCenter: parent.verticalCenter
- onClickItem:
- (model)=>{
- showSuccess(model.title)
- }
- }
- }
-
-
- FluArea{
- Layout.fillWidth: true
- height: 100
- paddings: 10
- Layout.topMargin: 20
-
- ColumnLayout{
- anchors.verticalCenter: parent.verticalCenter
- width:parent.width
- spacing: 10
-
- FluFilledButton{
- text: qsTr("Reset sample")
- onClicked:{
- var items = []
- for(var i=0;i<10;i++){
- items.push({title:"Item_"+(i+1)})
- }
- breadcrumb_2.items = items
- }
- }
-
- FluBreadcrumbBar{
- id:breadcrumb_2
- separator:">"
- spacing:8
- textSize:18
- Layout.fillWidth: true
- onClickItem:
- (model)=>{
- if(model.index+1!==count()){
- breadcrumb_2.remove(model.index+1,count()-model.index-1)
- }
- showSuccess(model.title)
- }
- }
- }
- }
-
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluBreadcrumbBar{
- width:parent.width
- separator:">"
- spacing:8
- textSize:18
- onClickItem: (model)=>{
-
- }
-}'
- }
-
-
-}
diff --git a/example/qml-Qt6/page/T_Buttons.qml b/example/qml-Qt6/page/T_Buttons.qml
deleted file mode 100644
index 7806e81c..00000000
--- a/example/qml-Qt6/page/T_Buttons.qml
+++ /dev/null
@@ -1,438 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import QtQuick.Controls.Basic
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Buttons")
-
- FluText{
- Layout.topMargin: 20
- text: qsTr("Support the Tab key to switch focus, and the Space key to perform click events")
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 68
- paddings: 10
- Layout.topMargin: 20
-
- FluTextButton{
- disabled: text_button_switch.checked
- text: qsTr("Text Button")
- onClicked: {
- showInfo("点击Text Button")
- }
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- }
- FluToggleSwitch{
- id: text_button_switch
- anchors{
- right: parent.right
- verticalCenter: parent.verticalCenter
- }
- text: qsTr("Disabled")
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluTextButton{
- text:"Text Button"
- onClicked: {
-
- }
-}'
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 68
- paddings: 10
- Layout.topMargin: 20
-
- FluButton{
- disabled: button_switch.checked
- text: qsTr("Standard Button")
- onClicked: {
- showInfo(qsTr("Click StandardButton"))
- }
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- }
- FluToggleSwitch{
- id: button_switch
- anchors{
- right: parent.right
- verticalCenter: parent.verticalCenter
- }
- text: qsTr("Disabled")
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluButton{
- text:"Standard Button"
- onClicked: {
-
- }
-}'
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 68
- Layout.topMargin: 20
- paddings: 10
-
- FluFilledButton{
- disabled: filled_button_switch.checked
- text: qsTr("Filled Button")
- onClicked: {
- showWarning(qsTr("Click FilledButton"))
- }
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- }
- FluToggleSwitch{
- id: filled_button_switch
- anchors{
- right: parent.right
- verticalCenter: parent.verticalCenter
- }
- text: qsTr("Disabled")
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluFilledButton{
- text:"Filled Button"
- onClicked: {
-
- }
-}'
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 68
- Layout.topMargin: 20
- paddings: 10
-
- FluToggleButton{
- disabled:toggle_button_switch.checked
- text: qsTr("Toggle Button")
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- }
- FluToggleSwitch{
- id: toggle_button_switch
- anchors{
- right: parent.right
- verticalCenter: parent.verticalCenter
- }
- text: qsTr("Disabled")
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluToggleButton{
- text:"Toggle Button"
- onClicked: {
- checked = !checked
- }
-}'
- }
-
- Timer{
- id: timer_progress
- interval: 200
- onTriggered: {
- btn_progress.progress = (btn_progress.progress + 0.1).toFixed(1)
- if(btn_progress.progress==1){
- timer_progress.stop()
- }else{
- timer_progress.start()
- }
- }
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 68
- Layout.topMargin: 20
- paddings: 10
-
- FluProgressButton{
- id: btn_progress
- disabled: progress_button_switch.checked
- text: qsTr("Progress Button")
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- onClicked: {
- btn_progress.progress = 0
- timer_progress.restart()
- }
- }
- FluToggleSwitch{
- id: progress_button_switch
- anchors{
- right: parent.right
- verticalCenter: parent.verticalCenter
- }
- text: qsTr("Disabled")
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluProgressButton{
- text:"Progress Button"
- onClicked: {
-
- }
-}'
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 68
- Layout.topMargin: 20
- paddings: 10
-
- FluLoadingButton{
- id: btn_loading
- loading: loading_button_switch.checked
- text: qsTr("Loading Button")
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- onClicked: {
-
- }
- }
- FluToggleSwitch{
- id: loading_button_switch
- checked: true
- anchors{
- right: parent.right
- verticalCenter: parent.verticalCenter
- }
- text: qsTr("Loading")
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluLoadingButton{
- text:"Loading Button"
- onClicked: {
-
- }
-}'
- }
-
-
- FluArea{
- Layout.fillWidth: true
- height: layout_icon_button.height + 30
- paddings: 10
- Layout.topMargin: 20
- Flow{
- id: layout_icon_button
- spacing: 10
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- right: icon_button_switch.left
- }
- FluIconButton{
- disabled: icon_button_switch.checked
- iconDelegate: Image{ sourceSize: Qt.size(40,40) ; width: 20; height: 20; source: "qrc:/example/res/image/ic_home_github.png" }
- onClicked:{
- showSuccess(qsTr("Click IconButton"))
- }
- }
- FluIconButton{
- iconSource: FluentIcons.ChromeCloseContrast
- disabled: icon_button_switch.checked
- iconSize: 15
- text: qsTr("IconOnly")
- display: Button.IconOnly
- onClicked:{
- showSuccess(qsTr("Button.IconOnly"))
- }
- }
- FluIconButton{
- iconSource: FluentIcons.ChromeCloseContrast
- disabled: icon_button_switch.checked
- iconSize: 15
- text: qsTr("TextOnly")
- display: Button.TextOnly
- onClicked:{
- showSuccess(qsTr("Button.TextOnly"))
- }
- }
- FluIconButton{
- iconSource: FluentIcons.ChromeCloseContrast
- disabled: icon_button_switch.checked
- iconSize: 15
- text: qsTr("TextBesideIcon")
- display: Button.TextBesideIcon
- onClicked:{
- showSuccess(qsTr("Button.TextBesideIcon"))
- }
- }
- FluIconButton{
- iconSource: FluentIcons.ChromeCloseContrast
- disabled: icon_button_switch.checked
- iconSize: 15
- text: qsTr("TextUnderIcon")
- display: Button.TextUnderIcon
- onClicked:{
- showSuccess(qsTr("Button.TextUnderIcon"))
- }
- }
- }
- FluToggleSwitch{
- id: icon_button_switch
- anchors{
- right: parent.right
- verticalCenter: parent.verticalCenter
- }
- text: qsTr("Disabled")
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluIconButton{
- iconSource:FluentIcons.ChromeCloseContrast
- onClicked: {
-
- }
-}'
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 68
- paddings: 10
- Layout.topMargin: 20
- FluDropDownButton{
- disabled: drop_down_button_switch.checked
- text: qsTr("DropDownButton")
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- FluMenuItem{
- text: qsTr("Menu_1")
- }
- FluMenuItem{
- text: qsTr("Menu_2")
- }
- FluMenuItem{
- text: qsTr("Menu_3")
- }
- FluMenuItem{
- text: qsTr("Menu_4")
- onClicked: {
-
- }
- }
- }
- FluToggleSwitch{
- id: drop_down_button_switch
- anchors{
- right: parent.right
- verticalCenter: parent.verticalCenter
- }
- text: qsTr("Disabled")
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluDropDownButton{
- text:"DropDownButton"
- FluMenuItem{
- text:"Menu_1"
- },
- FluMenuItem{
- text:"Menu_2"
- },
- FluMenuItem{
- text:"Menu_3"
- },
- FluMenuItem{
- text:"Menu_4"
- }
-}'
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 100
- paddings: 10
- Layout.topMargin: 20
- FluRadioButtons{
- spacing: 8
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- FluRadioButton{
- disabled:radio_button_switch.checked
- text: qsTr("Radio Button_1")
- }
- FluRadioButton{
- disabled:radio_button_switch.checked
- text: qsTr("Radio Button_2")
- }
- FluRadioButton{
- disabled:radio_button_switch.checked
- text: qsTr("Radio Button_3")
- }
- }
- FluToggleSwitch{
- id: radio_button_switch
- anchors{
- right: parent.right
- verticalCenter: parent.verticalCenter
- }
- text: qsTr("Disabled")
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluRadioButton{
- checked:true
- text:"Text Button"
- onClicked: {
-
- }
-}'
- }
-
-}
diff --git a/example/qml-Qt6/page/T_CalendarPicker.qml b/example/qml-Qt6/page/T_CalendarPicker.qml
deleted file mode 100644
index 1b28b644..00000000
--- a/example/qml-Qt6/page/T_CalendarPicker.qml
+++ /dev/null
@@ -1,37 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Window
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("CalendarPicker")
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 80
- paddings: 10
- ColumnLayout{
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- FluCalendarPicker{
- onAccepted:{
- showSuccess(current.toLocaleString())
- }
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluCalendarPicker{
-
-}'
- }
-
-}
diff --git a/example/qml-Qt6/page/T_Captcha.qml b/example/qml-Qt6/page/T_Captcha.qml
deleted file mode 100644
index 20f7275c..00000000
--- a/example/qml-Qt6/page/T_Captcha.qml
+++ /dev/null
@@ -1,60 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Captcha")
-
- FluCaptcha{
- id: captcha
- Layout.topMargin: 20
- ignoreCase:switch_case.checked
- MouseArea{
- anchors.fill: parent
- cursorShape: Qt.PointingHandCursor
- onClicked: {
- captcha.refresh()
- }
- }
- }
-
- FluButton{
- text: qsTr("Refresh")
- Layout.topMargin: 20
- onClicked: {
- captcha.refresh()
- }
- }
-
- FluToggleSwitch{
- id: switch_case
- text: qsTr("Ignore Case")
- checked: true
- Layout.topMargin: 10
- }
-
- RowLayout{
- spacing: 10
- Layout.topMargin: 10
- FluTextBox{
- id:text_box
- placeholderText: qsTr("Please enter a verification code")
- Layout.preferredWidth: 240
- }
- FluButton{
- text:"verify"
- onClicked: {
- var success = captcha.verify(text_box.text)
- if(success){
- showSuccess(qsTr("The verification code is correct"))
- }else{
- showError(qsTr("Error validation, please re-enter"))
- }
- }
- }
- }
-}
diff --git a/example/qml-Qt6/page/T_Carousel.qml b/example/qml-Qt6/page/T_Carousel.qml
deleted file mode 100644
index 401584f8..00000000
--- a/example/qml-Qt6/page/T_Carousel.qml
+++ /dev/null
@@ -1,147 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Carousel")
-
- ListModel{
- id:data_model
- ListElement{
- url:"qrc:/example/res/image/banner_1.jpg"
- }
- ListElement{
- url:"qrc:/example/res/image/banner_2.jpg"
- }
- ListElement{
- url:"qrc:/example/res/image/banner_3.jpg"
- }
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 370
- paddings: 10
- Layout.topMargin: 20
- Column{
- spacing: 15
- anchors{
- verticalCenter: parent.verticalCenter
- left:parent.left
- }
- FluText{
- text: qsTr("Carousel map, support infinite carousel, infinite swipe, and components implemented with ListView")
- }
- Item{
- width: 400
- height: 300
- FluShadow{
- radius: 8
- }
- FluCarousel{
- anchors.fill: parent
- delegate: Component{
- Image {
- anchors.fill: parent
- source: model.url
- asynchronous: true
- fillMode:Image.PreserveAspectCrop
- }
- }
- Layout.topMargin: 20
- Layout.leftMargin: 5
- Component.onCompleted: {
- model = [{url:"qrc:/example/res/image/banner_1.jpg"},{url:"qrc:/example/res/image/banner_2.jpg"},{url:"qrc:/example/res/image/banner_3.jpg"}]
- }
- }
- }
- }
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 340
- paddings: 10
- Layout.topMargin: 10
- Column{
- spacing: 15
- anchors{
- verticalCenter: parent.verticalCenter
- left:parent.left
- }
- Item{
- width: 400
- height: 300
- FluShadow{
- radius: 8
- }
- FluCarousel{
- anchors.fill: parent
- loopTime:1500
- indicatorGravity: Qt.AlignHCenter | Qt.AlignTop
- indicatorMarginTop:15
- delegate: Component{
- Item{
- anchors.fill: parent
- Image {
- anchors.fill: parent
- source: model.url
- asynchronous: true
- fillMode:Image.PreserveAspectCrop
- }
- Rectangle{
- height: 40
- width: parent.width
- anchors.bottom: parent.bottom
- color: "#33000000"
- FluText{
- anchors.fill: parent
- verticalAlignment: Qt.AlignVCenter
- horizontalAlignment: Qt.AlignHCenter
- text:model.title
- color: FluColors.Grey10
- font.pixelSize: 15
- }
- }
- }
- }
- Layout.topMargin: 20
- Layout.leftMargin: 5
- Component.onCompleted: {
- var arr = []
- arr.push({url:"qrc:/example/res/image/banner_1.jpg",title:"共同应对全球性问题"})
- arr.push({url:"qrc:/example/res/image/banner_2.jpg",title:"三小只全程没互动"})
- arr.push({url:"qrc:/example/res/image/banner_3.jpg",title:"有效投资扩大 激发增长动能"})
- model = arr
- }
- }
- }
-
- }
- }
-
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluCarousel{
- id:carousel
- width: 400
- height: 300
- delegate: Component{
- Image {
- anchors.fill: parent
- source: model.url
- asynchronous: true
- fillMode:Image.PreserveAspectCrop
- }
- }
- Component.onCompleted: {
- carousel.model = [{url:"qrc:/example/res/image/banner_1.jpg"},{url:"qrc:/example/res/image/banner_2.jpg"},{url:"qrc:/example/res/image/banner_3.jpg"}]
- }
-}'
- }
-}
diff --git a/example/qml-Qt6/page/T_CheckBox.qml b/example/qml-Qt6/page/T_CheckBox.qml
deleted file mode 100644
index 602270d4..00000000
--- a/example/qml-Qt6/page/T_CheckBox.qml
+++ /dev/null
@@ -1,114 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("CheckBox")
-
- FluArea{
- Layout.fillWidth: true
- height: 72
- paddings: 10
- Layout.topMargin: 20
-
- FluText{
- text: qsTr("A 2-state CheckBox")
- }
-
- Row{
- spacing: 30
- anchors{
- top: parent.top
- topMargin: 30
- }
- FluCheckBox{
- disabled: check_box_switch_two.checked
- }
- FluCheckBox{
- disabled: check_box_switch_two.checked
- text: qsTr("Right")
- }
- FluCheckBox{
- disabled: check_box_switch_two.checked
- text: qsTr("Left")
- textRight: false
- }
- }
- FluToggleSwitch{
- id:check_box_switch_two
- anchors{
- right: parent.right
- verticalCenter: parent.verticalCenter
- }
- text: qsTr("Disabled")
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluCheckBox{
- text:"Text"
-}'
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 72
- paddings: 10
- Layout.topMargin: 20
-
- FluText{
- text: qsTr("A 3-state CheckBox")
- }
-
- Row{
- spacing: 30
- anchors{
- top: parent.top
- topMargin: 30
- }
- FluCheckBox{
- property int count: 1
- text: qsTr("Three State")
- disabled: check_box_switch_three.checked
- clickListener: function(){
- var flag = count%3
- if(flag === 0){
- checked = false
- indeterminate = false
- }
- if(flag === 1){
- checked = true
- indeterminate = false
- }
- if(flag === 2){
- checked = true
- indeterminate = true
- }
- count++
- }
- }
- }
- FluToggleSwitch{
- id: check_box_switch_three
- anchors{
- right: parent.right
- verticalCenter: parent.verticalCenter
- }
- text: qsTr("Disabled")
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluCheckBox{
- text:"Text"
- indeterminate:true
-}'
- }
-
-}
diff --git a/example/qml-Qt6/page/T_Clip.qml b/example/qml-Qt6/page/T_Clip.qml
deleted file mode 100644
index 37d5f31e..00000000
--- a/example/qml-Qt6/page/T_Clip.qml
+++ /dev/null
@@ -1,106 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Controls
-import QtQuick.Window
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Clip")
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 380
- paddings: 10
-
- Column{
- spacing: 15
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- FluText{
- text: qsTr("Use with images (this component will have no effect under software rendering)")
- Layout.topMargin: 20
- }
- RowLayout{
- spacing: 14
- FluClip{
- width: 50
- height: 50
- radius: [25,0,25,25]
- Image {
- asynchronous: true
- anchors.fill: parent
- source: "qrc:/example/res/svg/avatar_1.svg"
- sourceSize: Qt.size(width,height)
- }
- }
- FluClip{
- width: 50
- height: 50
- radius: [10,10,10,10]
- Image {
- asynchronous: true
- anchors.fill: parent
- sourceSize: Qt.size(width,height)
- source: "qrc:/example/res/svg/avatar_2.svg"
- }
- }
- FluClip{
- width: 50
- height: 50
- radius: [25,25,25,25]
- Image {
- asynchronous: true
- anchors.fill: parent
- sourceSize: Qt.size(width,height)
- source: "qrc:/example/res/svg/avatar_3.svg"
- }
- }
- FluClip{
- width: 50
- height: 50
- radius: [0,25,25,25]
- Image {
- asynchronous: true
- anchors.fill: parent
- sourceSize: Qt.size(width,height)
- source: "qrc:/example/res/svg/avatar_4.svg"
- }
- }
- }
- FluClip{
- width: 1920/5
- height: 1200/5
- radius: [8,8,8,8]
- Image {
- asynchronous: true
- source: "qrc:/example/res/image/banner_1.jpg"
- anchors.fill: parent
- sourceSize: Qt.size(2*width,2*height)
- }
- Layout.topMargin: 20
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluClip{
- radius: [25,25,25,25]
- width: 50
- height: 50
- Image{
- asynchronous: true
- anchors.fill: parent
- source: "qrc:/example/res/svg/avatar_4.svg"
- sourceSize: Qt.size(width,height)
- }
-}'
- }
-
-
-}
diff --git a/example/qml-Qt6/page/T_ColorPicker.qml b/example/qml-Qt6/page/T_ColorPicker.qml
deleted file mode 100644
index ac8571b0..00000000
--- a/example/qml-Qt6/page/T_ColorPicker.qml
+++ /dev/null
@@ -1,43 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Window
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("ColorPicker")
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 60
- paddings: 10
- RowLayout{
- FluText{
- text: qsTr("Click to Select a Color - >")
- Layout.alignment: Qt.AlignVCenter
- }
- FluColorPicker{
- cancelText: qsTr("Cancel")
- okText: qsTr("OK")
- titleText: qsTr("Color Picker")
- editText: qsTr("Edit Color")
- redText: qsTr("Red")
- greenText: qsTr("Green")
- blueText: qsTr("Blue")
- opacityText: qsTr("Opacity")
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluColorPicker{
-
-}'
- }
-
-}
-
diff --git a/example/qml-Qt6/page/T_ComboBox.qml b/example/qml-Qt6/page/T_ComboBox.qml
deleted file mode 100644
index 02bd3b1c..00000000
--- a/example/qml-Qt6/page/T_ComboBox.qml
+++ /dev/null
@@ -1,104 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("ComboBox")
-
- FluArea{
- Layout.fillWidth: true
- height: 80
- paddings: 5
- Layout.topMargin: 20
- Column{
- spacing: 5
- anchors.verticalCenter: parent.verticalCenter
- FluText{
- text: "editable=false"
- x:10
- }
- FluComboBox {
- model: ListModel {
- id: model_1
- ListElement { text: "Banana" }
- ListElement { text: "Apple" }
- ListElement { text: "Coconut" }
- }
- }
- }
- }
-
- FluArea {
- Layout.fillWidth: true
- height: 80
- paddings: 5
- Layout.topMargin: 20
- Column{
- spacing: 5
- anchors.verticalCenter: parent.verticalCenter
- FluText{
- text: "disabled=true"
- x:10
- }
- FluComboBox {
- disabled: true
- model: ListModel {
- id: model_2
- ListElement { text: "Banana" }
- ListElement { text: "Apple" }
- ListElement { text: "Coconut" }
- }
- }
- }
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 80
- paddings: 10
- Layout.topMargin: 20
- Column{
- spacing: 5
- anchors.verticalCenter: parent.verticalCenter
- FluText{
- text: "editable=true"
- x:5
- }
- FluComboBox {
- editable: true
- model: ListModel {
- id: model_3
- ListElement { text: "Banana" }
- ListElement { text: "Apple" }
- ListElement { text: "Coconut" }
- }
- onAccepted: {
- if (find(editText) === -1)
- model_3.append({text: editText})
- }
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluComboBox{
- editable: true
- model: ListModel {
- id: model
- ListElement { text: "Banana" }
- ListElement { text: "Apple" }
- ListElement { text: "Coconut" }
- }
- onAccepted: {
- if (find(editText) === -1)
- model.append({text: editText})
- }
-}'
- }
-
-}
diff --git a/example/qml-Qt6/page/T_DatePicker.qml b/example/qml-Qt6/page/T_DatePicker.qml
deleted file mode 100644
index fa4e342f..00000000
--- a/example/qml-Qt6/page/T_DatePicker.qml
+++ /dev/null
@@ -1,75 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Window
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("TimePicker")
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 80
- paddings: 10
- ColumnLayout{
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- FluText{
- text: qsTr("showYear=true")
- }
- FluDatePicker{
- current: new Date()
- onAccepted: {
- showSuccess(current.toLocaleDateString())
- }
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluDatePicker{
-
-}'
- }
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 80
- paddings: 10
- ColumnLayout{
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- FluText{
- text: qsTr("showYear=false")
- }
- FluDatePicker{
- showYear: false
- yearText: qsTr("Year")
- monthText: qsTr("Month")
- dayText: qsTr("Day")
- cancelText: qsTr("Cancel")
- okText: qsTr("OK")
- onAccepted: {
- showSuccess(current.toLocaleDateString())
- }
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluDatePicker{
- showYear:false
-}'
- }
-
-}
diff --git a/example/qml-Qt6/page/T_Dialog.qml b/example/qml-Qt6/page/T_Dialog.qml
deleted file mode 100644
index 23bef093..00000000
--- a/example/qml-Qt6/page/T_Dialog.qml
+++ /dev/null
@@ -1,232 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Dialog")
-
- FluArea{
- Layout.fillWidth: true
- height: 68
- paddings: 10
- Layout.topMargin: 20
- FluButton{
- anchors.verticalCenter: parent.verticalCenter
- text: qsTr("Show Double Button Dialog")
- onClicked: {
- double_btn_dialog.open()
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluContentDialog{
- id:dialog
- title: qsTr("Friendly Reminder")
- message: qsTr("Are you sure you want to opt out?")
- negativeText: qsTr("Cancel")
- buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
- onNegativeClicked:{
- showSuccess(qsTr("Click the Cancel Button"))
- }
- positiveText: qsTr("OK")
- onPositiveClicked:{
- showSuccess(qsTr("Click the OK Button"))
- }
- }
- dialog.open()'
- }
-
- FluContentDialog{
- id:double_btn_dialog
- title: qsTr("Friendly Reminder")
- message: qsTr("Are you sure you want to opt out?")
- buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
- negativeText: qsTr("Cancel")
- onNegativeClicked: {
- showSuccess(qsTr("Click the Cancel Button"))
- }
- positiveText: qsTr("OK")
- onPositiveClicked:{
- showSuccess(qsTr("Click the OK Button"))
- }
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 68
- paddings: 10
- Layout.topMargin: 20
- FluButton{
- anchors.verticalCenter: parent.verticalCenter
- text: qsTr("Show Triple Button Dialog")
- onClicked: {
- triple_btn_dialog.open()
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluContentDialog{
- id: dialog
- title: qsTr("Friendly Reminder")
- message: qsTr("Are you sure you want to opt out?")
- negativeText: qsTr("Cancel")
- buttonFlags: FluContentDialogType.NeutralButton | FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
- negativeText: qsTr("Cancel")
- onNegativeClicked: {
- showSuccess(qsTr("Click the Cancel Button"))
- }
- positiveText: qsTr("OK")
- onPositiveClicked: {
- showSuccess(qsTr("Click the OK Button"))
- }
- neutralText: qsTr("Minimize")
- onNeutralClicked: {
- showSuccess(qsTr("Click Minimize"))
- }
- }
- dialog.open()'
- }
-
- FluContentDialog{
- id: triple_btn_dialog
- title: qsTr("Friendly Reminder")
- message: qsTr("Are you sure you want to opt out?")
- buttonFlags: FluContentDialogType.NeutralButton | FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
- negativeText: qsTr("Cancel")
- onNegativeClicked: {
- showSuccess(qsTr("Click the Cancel Button"))
- }
- positiveText: qsTr("OK")
- onPositiveClicked: {
- showSuccess(qsTr("Click the OK Button"))
- }
- neutralText: qsTr("Minimize")
- onNeutralClicked: {
- showSuccess(qsTr("Click Minimize"))
- }
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 100
- paddings: 10
- Layout.topMargin: 20
- FluButton{
- anchors.top: parent.top
- anchors.topMargin: 5
- text: qsTr("Custom Content Dialog")
- onClicked: {
- custom_btn_dialog.open()
- }
- }
- FluButton{
- anchors.top: parent.top
- anchors.topMargin: 48
- text: qsTr("Custom Content Dialog2")
- onClicked: {
- custom_btn_dialog2.open()
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluContentDialog{
- id: dialog
- title: qsTr("Friendly Reminder")
- message: qsTr("Data is loading, please wait...")
- negativeText: qsTr("Unload")
- contentDelegate: Component{
- Item{
- width: parent.width
- height: 80
- FluProgressRing{
- anchors.centerIn: parent
- }
- }
- }
- onNegativeClicked: {
- showSuccess(qsTr("Click the Cancel Button"))
- }
- positiveText :qsTr("OK")
- onPositiveClicked: {
- showSuccess(qsTr("Click the OK Button"))
- }
- }
- dialog.open()'
- }
-
- FluContentDialog{
- id: custom_btn_dialog
- title: qsTr("Friendly Reminder")
- message: qsTr("Data is loading, please wait...")
- negativeText: qsTr("Unload")
- contentDelegate: Component{
- Item{
- implicitWidth: parent.width
- implicitHeight: 80
- FluProgressRing{
- anchors.centerIn: parent
- }
- }
- }
- onNegativeClicked: {
- showSuccess(qsTr("Click the Cancel Button"))
- }
- positiveText: qsTr("OK")
- onPositiveClickListener: function(){
- showError(qsTr("Test the InfoBar level on top of the Popup"))
- }
- }
-
- FluContentDialog{
- id:custom_btn_dialog2
- title: qsTr("Line Chart")
- contentDelegate: Component{
- Item{
- implicitWidth: parent.width
- implicitHeight: 300
- FluChart{
- anchors.fill: parent
- chartType: 'line'
- chartData: { return {
- labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
- datasets: [{
- label: 'My First Dataset',
- data: [65, 59, 80, 81, 56, 55, 40],
- fill: false,
- borderColor: 'rgb(75, 192, 192)',
- tension: 0.1
- }]
- }
- }
- chartOptions: { return {
- maintainAspectRatio: false,
- title: {
- display: true,
- text: 'Chart.js Line Chart - Stacked'
- },
- tooltips: {
- mode: 'index',
- intersect: false
- }
- }
- }
- }
- }
- }
- buttonFlags: FluContentDialogType.PositiveButton
- positiveText: qsTr("OK")
- onPositiveClicked: {
- showSuccess(qsTr("Click the OK Button"))
- }
- }
-}
diff --git a/example/qml-Qt6/page/T_Expander.qml b/example/qml-Qt6/page/T_Expander.qml
deleted file mode 100644
index 602f67c9..00000000
--- a/example/qml-Qt6/page/T_Expander.qml
+++ /dev/null
@@ -1,110 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Window
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Expander")
-
- FluArea{
- Layout.fillWidth: true
- height: layout_column.height+20
- paddings: 10
- Layout.topMargin: 20
- Column{
- id:layout_column
- spacing: 15
- anchors{
- top:parent.top
- left:parent.left
- }
-
- FluExpander{
- headerText: qsTr("Open a radio box")
- Layout.topMargin: 20
- Item{
- anchors.fill: parent
- FluRadioButtons{
- spacing: 8
- anchors{
- top: parent.top
- left: parent.left
- topMargin: 15
- leftMargin: 15
- }
- FluRadioButton{
- text:"Radio Button_1"
- }
- FluRadioButton{
- text:"Radio Button_2"
- }
- FluRadioButton{
- text:"Radio Button_3"
- }
- }
- }
- }
-
- FluExpander{
- Layout.topMargin: 20
- headerText: qsTr("Open a sliding text box")
- Item{
- anchors.fill: parent
- Flickable{
- id:scrollview
- width: parent.width
- height: parent.height
- contentWidth: width
- boundsBehavior: Flickable.StopAtBounds
- contentHeight: text_info.height
- ScrollBar.vertical: FluScrollBar {}
- FluText{
- id:text_info
- width: scrollview.width
- wrapMode: Text.WrapAnywhere
- padding: 14
- text: qsTr("Permit me to observe: the late emperor was taken from us before he could finish his life`s work, the restoration of Han. Today, the empire is still divided in three, and our very survival is threatened. Yet still the officials at court and the soldiers throughout the realm remain loyal to you, your majesty. Because they remember the late emperor, all of them, and they wish to repay his kindness in service to you. This is the moment to extend your divine influence, to honour the memory of the late Emperor and strengthen the morale of your officers. It is not time to listen to bad advice, or close your ears to the suggestions of loyal men.
-The court and the administration are as one. Both must be judged by one standard. Those who are loyal and good must get what they deserve, but so must the evil-doers who break the law. This will demonstrate the justice of your rule. There cannot be one law for the court and another for the administration.
-Counselors and attendants like Guo Youzhi, Fei Yi, and Dong Yun are all reliable men, loyal of purpose and pure in motive. The late Emperor selected them for office so that they would serve you after his death.These are the men who should be consulted on all palace affairs. Xiang Chong has proved himself a fine general in battle, and the late Emperor believed in him. That is why the assembly has recommended him for overall command. It will keep the troops happy if he is consulted on all military matters.
-Xiang Chong has proved himself a fine general in battle, and the late Emperor believed in him. That is why the assembly has recommended him for overall command. It will keep the troops happy if he is consulted on all military matters.
-The emperors of the Western Han chose their courtiers wisely, and their dynasty flourished. The emperors of the Eastern Han chose poorly, and they doomed the empire to ruin. Whenever the late Emperor discussed this problem with me, he lamented the failings of Emperors Huan and Ling. Advisors like Guo Youzhi, Fei Yi, Chen Zhen, Zhang Yi, and Jiang Wan – these are all men of great integrity and devotion. I encourage you to trust them, your majesty, if the house of Han is to rise again.
-I begin as a common man, farming in my fields in Nanyang, doing what I could to survive in an age of chaos. I never had any interest in making a name for myself as a noble. The late Emperor was not ashamed to visit my cottage and seek my advice. Grateful for his regard, I responded to his appeal and threw myself into his service. Now twenty-one years has passed, the late Emperor always appreciated my caution and, in his final days, entrusted me with his cause.
-Since that moment, I have been tormented day and night by the fear that I might let him down. That is why I crossed the Lu river at the height of summer, and entered the wastelands beyond. Now the south has been subdued, and our forces are fully armed.I should lead our soldiers to conquer the northern heartland and attempt to remove the hateful traitors, restore the house of Han, and return it to the former capital.This the way I mean to honor my debt to the late Emperor and fulfill my duty to you. Guo Youzhi, Fei Yi, and Dong Yun are the ones who should be making policy decisions and recommendations.
-My only desire is to be permitted to drive out the traitors and restore the Han. If I should let you down, punish my offense and report it to the spirit of the late Emperor. If those three advisors should fail in their duties, then they should be punished for their negligence.Your Majesty, consider your course of action carefully. Seek out good advice, and never forget the late Emperor. I depart now on a long expedition, and I will be forever grateful if you heed my advice. Blinded by my own tears, I know not what I write.")
- }
- }
- }
- }
- }
- }
-
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluExpander{
- headerText: qsTr("Open a radio box")
- Item{
- anchors.fill: parent
- Flickable{
- width: parent.width
- height: parent.height
- contentWidth: width
- contentHeight: text_info.height
- ScrollBar.vertical: FluScrollBar {}
- FluText{
- id:text_info
- width: scrollview.width
- wrapMode: Text.WrapAnywhere
- padding: 14
- text: qsTr("Permit me to observe: the late emperor was taken from us before he could finish his life`s work...")
- }
- }
- }
-}'
- }
-
-
-}
diff --git a/example/qml-Qt6/page/T_FlipView.qml b/example/qml-Qt6/page/T_FlipView.qml
deleted file mode 100644
index 4d75df9e..00000000
--- a/example/qml-Qt6/page/T_FlipView.qml
+++ /dev/null
@@ -1,117 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Window
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("FlipView")
-
- FluArea{
- Layout.fillWidth: true
- height: 340
- paddings: 10
- Layout.topMargin: 20
- ColumnLayout{
- anchors.verticalCenter: parent.verticalCenter
- FluText{
- text: qsTr("Horizontal FlipView")
- }
- FluFlipView{
- Image{
- source: "qrc:/example/res/image/banner_1.jpg"
- asynchronous: true
- fillMode:Image.PreserveAspectCrop
- }
- Image{
- source: "qrc:/example/res/image/banner_2.jpg"
- asynchronous: true
- fillMode:Image.PreserveAspectCrop
- }
- Image{
- source: "qrc:/example/res/image/banner_3.jpg"
- asynchronous: true
- fillMode:Image.PreserveAspectCrop
- }
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluFlipView{
- Image{
- source: "qrc:/example/res/image/banner_1.jpg"
- asynchronous: true
- fillMode:Image.PreserveAspectCrop
- }
- Image{
- source: "qrc:/example/res/image/banner_1.jpg"
- asynchronous: true
- fillMode:Image.PreserveAspectCrop
- }
- Image{
- source: "qrc:/example/res/image/banner_1.jpg"
- asynchronous: true
- fillMode:Image.PreserveAspectCrop
- }
-}
-'
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 340
- paddings: 10
- Layout.topMargin: 20
- ColumnLayout{
- anchors.verticalCenter: parent.verticalCenter
- FluText{
- text: qsTr("Vertical FlipView")
- }
- FluFlipView{
- vertical:true
- Image{
- source: "qrc:/example/res/image/banner_1.jpg"
- asynchronous: true
- fillMode:Image.PreserveAspectCrop
- }
- Image{
- source: "qrc:/example/res/image/banner_2.jpg"
- asynchronous: true
- fillMode:Image.PreserveAspectCrop
- }
- Image{
- source: "qrc:/example/res/image/banner_3.jpg"
- asynchronous: true
- fillMode:Image.PreserveAspectCrop
- }
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluFlipView{
- vertical:true
- Image{
- source: "qrc:/example/res/image/banner_1.jpg"
- asynchronous: true
- fillMode:Image.PreserveAspectCrop
- }
- Image{
- source: "qrc:/example/res/image/banner_1.jpg"
- asynchronous: true
- fillMode:Image.PreserveAspectCrop
- }
- Image{
- source: "qrc:/example/res/image/banner_1.jpg"
- asynchronous: true
- fillMode:Image.PreserveAspectCrop
- }
-}
-'
- }
-}
diff --git a/example/qml-Qt6/page/T_Home.qml b/example/qml-Qt6/page/T_Home.qml
deleted file mode 100644
index 3437c4ea..00000000
--- a/example/qml-Qt6/page/T_Home.qml
+++ /dev/null
@@ -1,294 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../global"
-import "../window"
-
-
-FluScrollablePage{
-
- launchMode: FluPageType.SingleTask
- animDisabled: true
-
- FluentInitalizrWindow{
- id:fluent_initalizr
- }
-
- ListModel{
- id: model_header
- ListElement{
- icon: "qrc:/example/res/image/ic_home_github.png"
- title: qsTr("FluentUI GitHub")
- desc: qsTr("The latest FluentUI controls and styles for your applications.")
- url: "https://github.com/zhuzichu520/FluentUI"
- clicked: function(model){
- Qt.openUrlExternally(model.url)
- }
- }
- ListElement{
- icon: "qrc:/example/res/image/favicon.ico"
- title: qsTr("FluentUI Initalizr")
- desc: qsTr("FluentUI Initializr is a Tool that helps you create and customize Fluent UI projects with various options.")
- url: "https://github.com/zhuzichu520/FluentUI"
- clicked: function(model){
- fluent_initalizr.showDialog()
- }
- }
- }
- Item{
- Layout.fillWidth: true
- Layout.preferredHeight: 320
- Image {
- id: bg
- fillMode:Image.PreserveAspectCrop
- anchors.fill: parent
- verticalAlignment: Qt.AlignTop
- sourceSize: Qt.size(960,640)
- source: "qrc:/example/res/image/bg_home_header.png"
- }
- Rectangle{
- anchors.fill: parent
- gradient: Gradient{
- GradientStop { position: 0.8; color: FluTheme.dark ? Qt.rgba(0,0,0,0) : Qt.rgba(1,1,1,0) }
- GradientStop { position: 1.0; color: FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1) }
- }
- }
- FluText{
- text:"FluentUI Gallery"
- font: FluTextStyle.TitleLarge
- anchors{
- top: parent.top
- left: parent.left
- topMargin: 20
- leftMargin: 20
- }
- }
- Component{
- id:com_grallery
- Item{
- id: control
- width: 220
- height: 240
- FluShadow{
- radius:5
- anchors.fill: item_content
- }
- FluClip{
- id:item_content
- radius: [5,5,5,5]
- width: 200
- height: 220
- anchors.centerIn: parent
- FluAcrylic{
- anchors.fill: parent
- tintColor: FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
- target: bg
- tintOpacity: FluTheme.dark ? 0.8 : 0.9
- blurRadius : 40
- targetRect: Qt.rect(list.x-list.contentX+10+(control.width)*index,list.y+10,width,height)
- }
- Rectangle{
- anchors.fill: parent
- radius: 5
- color:FluTheme.itemHoverColor
- visible: item_mouse.containsMouse
- }
- Rectangle{
- anchors.fill: parent
- radius: 5
- color:Qt.rgba(0,0,0,0.0)
- visible: !item_mouse.containsMouse
- }
- ColumnLayout{
- Image {
- Layout.topMargin: 20
- Layout.leftMargin: 20
- Layout.preferredWidth: 50
- Layout.preferredHeight: 50
- source: model.icon
- }
- FluText{
- text: model.title
- font: FluTextStyle.Body
- Layout.topMargin: 20
- Layout.leftMargin: 20
- }
- FluText{
- text: model.desc
- Layout.topMargin: 5
- Layout.preferredWidth: 160
- Layout.leftMargin: 20
- color: FluColors.Grey120
- font.pixelSize: 12
- wrapMode: Text.WrapAnywhere
- }
- }
- FluIcon{
- iconSource: FluentIcons.OpenInNewWindow
- iconSize: 15
- anchors{
- bottom: parent.bottom
- right: parent.right
- rightMargin: 10
- bottomMargin: 10
- }
- }
- MouseArea{
- id:item_mouse
- anchors.fill: parent
- hoverEnabled: true
- onWheel:
- (wheel)=>{
- if (wheel.angleDelta.y > 0) scrollbar_header.decrease()
- else scrollbar_header.increase()
- }
- onClicked: {
- model.clicked(model)
- }
- }
- }
- }
- }
-
- ListView{
- id: list
- anchors{
- left: parent.left
- right: parent.right
- bottom: parent.bottom
- }
- orientation: ListView.Horizontal
- height: 240
- model: model_header
- header: Item{height: 10;width: 10}
- footer: Item{height: 10;width: 10}
- ScrollBar.horizontal: FluScrollBar{
- id: scrollbar_header
- }
- clip: false
- delegate: com_grallery
- }
- }
-
- Component{
- id:com_item
- Item{
- property string desc: modelData.extra.desc
- width: 320
- height: 120
- FluArea{
- radius: 8
- width: 300
- height: 100
- anchors.centerIn: parent
- Rectangle{
- anchors.fill: parent
- radius: 8
- color:{
- if(item_mouse.containsMouse){
- return FluTheme.itemHoverColor
- }
- return FluTheme.itemNormalColor
- }
- }
- Image{
- id:item_icon
- height: 40
- width: 40
- source: modelData.extra.image
- anchors{
- left: parent.left
- leftMargin: 20
- verticalCenter: parent.verticalCenter
- }
- }
- FluText{
- id:item_title
- text:modelData.title
- font: FluTextStyle.BodyStrong
- anchors{
- left: item_icon.right
- leftMargin: 20
- top: item_icon.top
- }
- }
- FluText{
- id:item_desc
- text:desc
- color:FluColors.Grey120
- wrapMode: Text.WrapAnywhere
- elide: Text.ElideRight
- font: FluTextStyle.Caption
- maximumLineCount: 2
- anchors{
- left: item_title.left
- right: parent.right
- rightMargin: 20
- top: item_title.bottom
- topMargin: 5
- }
- }
-
- Rectangle{
- height: 12
- width: 12
- radius: 6
- color: FluTheme.primaryColor
- anchors{
- right: parent.right
- top: parent.top
- rightMargin: 14
- topMargin: 14
- }
- }
-
- MouseArea{
- id:item_mouse
- anchors.fill: parent
- hoverEnabled: true
- onClicked: {
- ItemsOriginal.startPageByItem(modelData)
- }
- }
- }
- }
- }
-
- FluText{
- text: "Recently added samples"
- font: FluTextStyle.Title
- Layout.topMargin: 20
- Layout.leftMargin: 20
- }
-
- GridView{
- Layout.fillWidth: true
- Layout.preferredHeight: contentHeight
- cellHeight: 120
- cellWidth: 320
- model:ItemsOriginal.getRecentlyAddedData()
- interactive: false
- delegate: com_item
- }
-
- FluText{
- text: "Recently updated samples"
- font: FluTextStyle.Title
- Layout.topMargin: 20
- Layout.leftMargin: 20
- }
-
- GridView{
- Layout.fillWidth: true
- Layout.preferredHeight: contentHeight
- cellHeight: 120
- cellWidth: 320
- interactive: false
- model: ItemsOriginal.getRecentlyUpdatedData()
- delegate: com_item
- }
-
-}
-
diff --git a/example/qml-Qt6/page/T_Image.qml b/example/qml-Qt6/page/T_Image.qml
deleted file mode 100644
index be2745ea..00000000
--- a/example/qml-Qt6/page/T_Image.qml
+++ /dev/null
@@ -1,48 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Image")
-
- FluArea{
- Layout.fillWidth: true
- height: 260
- paddings: 10
- Layout.topMargin: 20
- Column{
- spacing: 15
- anchors{
- verticalCenter: parent.verticalCenter
- left:parent.left
- }
- FluImage{
- width: 384
- height: 240
- source: "https://gitee.com/zhu-zichu/zhu-zichu/raw/74f075efe2f8d3c3bb7ba3c2259e403450e4050b/image/banner_4.jpg"
- onStatusChanged:{
- if(status === Image.Error){
- showError(qsTr("The image failed to load, please reload"))
- }
- }
- clickErrorListener: function(){
- source = "https://gitee.com/zhu-zichu/zhu-zichu/raw/74f075efe2f8d3c3bb7ba3c2259e403450e4050b/image/banner_1.jpg"
- }
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluImage{
- width: 400
- height: 300
- source: "https://gitee.com/zhu-zichu/zhu-zichu/raw/74f075efe2f8d3c3bb7ba3c2259e403450e4050b/image/banner_1.jpg"
-}'
- }
-
-}
diff --git a/example/qml-Qt6/page/T_InfoBar.qml b/example/qml-Qt6/page/T_InfoBar.qml
deleted file mode 100644
index 89406e2e..00000000
--- a/example/qml-Qt6/page/T_InfoBar.qml
+++ /dev/null
@@ -1,72 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("InfoBar")
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 270
- paddings: 10
- ColumnLayout{
- spacing: 14
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- FluButton{
- text: qsTr("Info")
- onClicked: {
- showInfo(qsTr("This is an InfoBar in the Info Style"))
- }
- }
- FluButton{
- text: qsTr("Warning")
- onClicked: {
- showWarning(qsTr("This is an InfoBar in the Warning Style"))
- }
- }
- FluButton{
- text:"Error"
- onClicked: {
- showError(qsTr("This is an InfoBar in the Error Style"))
- }
- }
- FluButton{
- text:"Success"
- onClicked: {
- showSuccess(qsTr("This is an InfoBar in the Success Style"))
- }
- }
- FluButton{
- text: qsTr("InfoBar that needs to be turned off manually")
- onClicked: {
- showInfo(qsTr("This is an InfoBar in the Info Style"),0,qsTr("Manual shutdown is supported"))
- }
- }
- FluButton{
- text:"Loading"
- onClicked: {
- showLoading()
- }
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'showInfo(qsTr("This is an InfoBar in the Info Style"))
-
-showWarning(qsTr("This is an InfoBar in the Warning Style"))
-
-showError(qsTr("This is an InfoBar in the Error Style"))
-
-showSuccess(qsTr("This is an InfoBar in the Success Style"))'
- }
-}
diff --git a/example/qml-Qt6/page/T_Menu.qml b/example/qml-Qt6/page/T_Menu.qml
deleted file mode 100644
index 56b5ef09..00000000
--- a/example/qml-Qt6/page/T_Menu.qml
+++ /dev/null
@@ -1,171 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Menu")
-
- FluMenu {
- id:menu
- title: qsTr("File")
- Action { text: qsTr("New...")}
- Action { text: qsTr("Open...") }
- Action { text: qsTr("Save") }
- FluMenuSeparator { }
- FluMenuItem{
- text: qsTr("Quit")
- onTriggered: {
- showError("Quit")
- }
- }
- FluMenuItem{
- text: qsTr("Search")
- iconSource: FluentIcons.Zoom
- iconSpacing: 3
- onTriggered: {
- showError(qsTr("Search"))
- }
- }
- Action {
- text: qsTr("Disable")
- enabled:false
- onTriggered: {
- showError(qsTr("Disable"))
- }
- }
- FluMenuSeparator { }
- Action { text: qsTr("Check");checkable: true;checked: true}
- FluMenu{
- title: qsTr("Save As...")
- Action { text: qsTr("Doc") }
- Action { text: qsTr("PDF") }
- }
- }
-
-
- FluArea{
- Layout.fillWidth: true
- height: 100
- paddings: 10
- Layout.topMargin: 20
- Column{
- id: layout_column
- spacing: 15
- anchors{
- verticalCenter: parent.verticalCenter
- left:parent.left
- }
-
- FluText{
- text: qsTr("Menu")
- }
-
- FluButton{
- text: qsTr("Show Menu Popup")
- Layout.topMargin: 20
- onClicked:{
- menu.popup()
- }
- }
-
-
- }
- }
-
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluMenu{
- id:menu
- FluMenuItem:{
- text:"删除"
- onClicked: {
- showError("删除")
- }
- }
- FluMenuItem:{
- text:"修改"
- onClicked: {
- showInfo("修改")
- }
- }
-}
-menu.popup()
-'
- }
-
-
- FluArea{
- Layout.fillWidth: true
- height: 100
- paddings: 10
- Layout.topMargin: 20
- Column{
- spacing: 15
- anchors{
- verticalCenter: parent.verticalCenter
- left:parent.left
- }
-
- FluText{
- text: qsTr("MenuBar")
- }
-
- FluMenuBar {
- id:menu_bar
- FluMenu {
- title: qsTr("File")
- Action { text: qsTr("New...") }
- Action { text: qsTr("Open...") }
- Action { text: qsTr("Save") }
- FluMenuSeparator { }
- Action { text: qsTr("Quit") }
- Action {
- text: qsTr("Disable")
- enabled:false
- }
- FluMenu{
- title: qsTr("Save As...")
- Action { text: qsTr("Doc") }
- Action { text: qsTr("PDF") }
- }
- }
- FluMenu {
- title: qsTr("Edit")
- Action { text: qsTr("Cut") }
- Action { text: qsTr("Copy") }
- Action { text: qsTr("Paste") }
- }
- FluMenu {
- title: qsTr("Help")
- Action { text: qsTr("About") }
- }
- }
-
- }
- }
-
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluMenuBar{
- id:menu
- FluMenu:{
- title:"File"
- Action { text: qsTr("New...") }
- }
- FluMenu:{
- title:"Edit"
- Action { text: qsTr("Cut") }
- Action { text: qsTr("Copy") }
- Action { text: qsTr("Paste") }
- }
-}
-menu.popup()
-'
- }
-}
diff --git a/example/qml-Qt6/page/T_MultiWindow.qml b/example/qml-Qt6/page/T_MultiWindow.qml
deleted file mode 100644
index 1f21a789..00000000
--- a/example/qml-Qt6/page/T_MultiWindow.qml
+++ /dev/null
@@ -1,186 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- property string password: ""
- property var loginPageRegister: registerForWindowResult("/login")
-
- title: qsTr("MultiWindow")
-
- Connections{
- target: loginPageRegister
- function onResult(data)
- {
- password = data.password
- }
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 86
- paddings: 10
- Layout.topMargin: 20
- Column{
- spacing: 15
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- FluText{
- text: qsTr("Standard mode window,a new window is created every time")
- }
- FluButton{
- text: qsTr("Create Window")
- onClicked: {
- FluApp.navigate("/standardWindow")
- }
- }
- }
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 86
- paddings: 10
- Layout.topMargin: 10
- Column{
- spacing: 15
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- FluText{
- text: qsTr("SingleTask mode window,If a window exists, this activates the window")
- textFormat: Text.RichText
- }
- FluButton{
- text: qsTr("Create Window")
- onClicked: {
- FluApp.navigate("/singleTaskWindow")
- }
- }
- }
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 86
- paddings: 10
- Layout.topMargin: 10
- Column{
- spacing: 15
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- FluText{
- text: qsTr("SingleInstance mode window,If the window exists, destroy the window and create a new window")
- }
- FluButton{
- text: qsTr("Create Window")
- onClicked: {
- FluApp.navigate("/singleInstanceWindow")
- }
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluWindow{
- //launchMode: FluWindowType.Standard
- //launchMode: FluWindowType.SingleTask
- launchMode: FluWindowType.SingleInstance
-}
-'
- }
-
-
- FluArea{
- Layout.fillWidth: true
- height: 100
- paddings: 10
- Layout.topMargin: 20
- Column{
- spacing: 15
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- FluText{
- text: qsTr("Create the window without carrying any parameters")
- }
- FluButton{
- text: qsTr("Create Window")
- onClicked: {
- FluApp.navigate("/about")
- }
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluButton{
- text: qsTr("Create Window")
- onClicked: {
- FluApp.navigate("/about")
- }
-}
-'
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 130
- paddings: 10
- Layout.topMargin: 20
-
- Column{
- spacing: 15
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- FluText{
- text: qsTr("Create a window with the parameter username: zhuzichu")
- }
- FluButton{
- text: qsTr("Create Window")
- onClicked: {
- loginPageRegister.launch({username:"zhuzichu"})
- }
- }
- FluText{
- text:qsTr("Login Window Returned Password - >")+password
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'property var loginPageRegister: registerForWindowResult("/login")
-
-Connections{
- target: loginPageRegister
- function onResult(data)
- {
- password = data.password
- }
-}
-
-FluButton{
- text: qsTr("Create Window")
- onClicked: {
- loginPageRegister.launch({username:"zhuzichu"})
- }
-}
-'
- }
-
-}
diff --git a/example/qml-Qt6/page/T_Network.qml b/example/qml-Qt6/page/T_Network.qml
deleted file mode 100644
index f4313c92..00000000
--- a/example/qml-Qt6/page/T_Network.qml
+++ /dev/null
@@ -1,560 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import Qt.labs.platform
-import "../component"
-
-FluContentPage{
-
- id:root
- title: qsTr("Network")
-
- FluNetworkCallable{
- id:callable
- onStart: {
- showLoading()
- }
- onFinish: {
- hideLoading()
- }
- onError:
- (status,errorString,result)=>{
- console.debug(status+";"+errorString+";"+result)
- }
- onCache:
- (result)=>{
- text_info.text = result
- }
- onSuccess:
- (result)=>{
- text_info.text = result
- }
- }
-
- Flickable{
- id:layout_flick
- width: 200
- clip: true
- anchors{
- top: parent.top
- topMargin: 20
- bottom: parent.bottom
- left: parent.left
- }
- boundsBehavior: Flickable.StopAtBounds
- ScrollBar.vertical: FluScrollBar {}
- contentHeight:layout_column.height
- Column{
- spacing: 2
- id:layout_column
- width: parent.width
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Get"
- onClicked: {
- text_info.text = ""
- FluNetwork.get("https://httpbingo.org/get")
- .addQuery("name","孙悟空")
- .addQuery("age",500)
- .addQuery("address","花果山水帘洞")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Head"
- onClicked: {
- text_info.text = ""
- FluNetwork.head("https://httpbingo.org/head")
- .addQuery("name","孙悟空")
- .addQuery("age",500)
- .addQuery("address","花果山水帘洞")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Post Body"
- onClicked: {
- text_info.text = ""
- FluNetwork.postBody("https://httpbingo.org/post")
- .setBody("花果山水帘洞美猴王齐天大圣孙悟空")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Post Form"
- onClicked: {
- text_info.text = ""
- FluNetwork.postForm("https://httpbingo.org/post")
- .add("name","孙悟空")
- .add("age",500)
- .add("address","花果山水帘洞")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Post JSON"
- onClicked: {
- text_info.text = ""
- FluNetwork.postJson("https://httpbingo.org/post")
- .add("name","孙悟空")
- .add("age",500)
- .add("address","花果山水帘洞")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Post JSON Array"
- onClicked: {
- text_info.text = ""
- FluNetwork.postJsonArray("https://httpbingo.org/post")
- .add("name","孙悟空")
- .add("age",500)
- .add("address","花果山水帘洞")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Put Body"
- onClicked: {
- text_info.text = ""
- FluNetwork.putBody("https://httpbingo.org/put")
- .setBody("花果山水帘洞美猴王齐天大圣孙悟空")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Put Form"
- onClicked: {
- text_info.text = ""
- FluNetwork.putForm("https://httpbingo.org/put")
- .add("name","孙悟空")
- .add("age",500)
- .add("address","花果山水帘洞")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Put JSON"
- onClicked: {
- text_info.text = ""
- FluNetwork.putJson("https://httpbingo.org/put")
- .add("name","孙悟空")
- .add("age",500)
- .add("address","花果山水帘洞")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Put JSON Array"
- onClicked: {
- text_info.text = ""
- FluNetwork.putJsonArray("https://httpbingo.org/put")
- .add("name","孙悟空")
- .add("age",500)
- .add("address","花果山水帘洞")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Patch Body"
- onClicked: {
- text_info.text = ""
- FluNetwork.patchBody("https://httpbingo.org/patch")
- .setBody("花果山水帘洞美猴王齐天大圣孙悟空")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Patch Form"
- onClicked: {
- text_info.text = ""
- FluNetwork.patchForm("https://httpbingo.org/patch")
- .add("name","孙悟空")
- .add("age",500)
- .add("address","花果山水帘洞")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Patch JSON"
- onClicked: {
- text_info.text = ""
- FluNetwork.patchJson("https://httpbingo.org/patch")
- .add("name","孙悟空")
- .add("age",500)
- .add("address","花果山水帘洞")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Patch JSON Array"
- onClicked: {
- text_info.text = ""
- FluNetwork.patchJsonArray("https://httpbingo.org/patch")
- .add("name","孙悟空")
- .add("age",500)
- .add("address","花果山水帘洞")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Delete Body"
- onClicked: {
- text_info.text = ""
- FluNetwork.deleteBody("https://httpbingo.org/delete")
- .setBody("花果山水帘洞美猴王齐天大圣孙悟空")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Delete Form"
- onClicked: {
- text_info.text = ""
- FluNetwork.deleteForm("https://httpbingo.org/delete")
- .add("name","孙悟空")
- .add("age",500)
- .add("address","花果山水帘洞")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Delete JSON"
- onClicked: {
- text_info.text = ""
- FluNetwork.deleteJson("https://httpbingo.org/delete")
- .add("name","孙悟空")
- .add("age",500)
- .add("address","花果山水帘洞")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Delete JSON Array"
- onClicked: {
- text_info.text = ""
- FluNetwork.deleteJsonArray("https://httpbingo.org/delete")
- .add("name","孙悟空")
- .add("age",500)
- .add("address","花果山水帘洞")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Open Log"
- onClicked: {
- text_info.text = ""
- FluNetwork.postJson("https://httpbingo.org/post")
- .add("name","孙悟空")
- .add("age",500)
- .add("address","花果山水帘洞")
- .openLog(true)
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Custom Header"
- onClicked: {
- text_info.text = ""
- FluNetwork.postJson("https://httpbingo.org/post")
- .addHeader("os","PC")
- .addHeader("version","1.0.0")
- .add("name","孙悟空")
- .add("age",500)
- .add("address","花果山水帘洞")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "RequestFailedReadCache"
- onClicked: {
- text_info.text = ""
- FluNetwork.postJson("https://httpbingo.org/post")
- .setCacheMode(FluNetworkType.RequestFailedReadCache)
- .add("name","孙悟空")
- .add("age",500)
- .add("address","花果山水帘洞")
- .add("cacheMode","RequestFailedReadCache")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "IfNoneCacheRequest"
- onClicked: {
- text_info.text = ""
- FluNetwork.postJson("https://httpbingo.org/post")
- .setCacheMode(FluNetworkType.IfNoneCacheRequest)
- .add("name","孙悟空")
- .add("age",500)
- .add("address","花果山水帘洞")
- .add("cacheMode","IfNoneCacheRequest")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "FirstCacheThenRequest"
- onClicked: {
- text_info.text = ""
- FluNetwork.postJson("https://httpbingo.org/post")
- .setCacheMode(FluNetworkType.FirstCacheThenRequest)
- .add("name","孙悟空")
- .add("age",500)
- .add("address","花果山水帘洞")
- .add("cacheMode","FirstCacheThenRequest")
- .bind(root)
- .go(callable)
- }
- }
- FluButton{
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Timeout And Retry"
- onClicked: {
- text_info.text = ""
- FluNetwork.postJson("https://httpbingo.org/post")
- .setTimeout(5000)
- .setRetry(3)
- .add("name","孙悟空")
- .add("age",500)
- .add("address","花果山水帘洞")
- .add("timeout","5000")
- .add("retry","3")
- .bind(root)
- .go(callable)
- }
- }
- FluProgressButton{
- id:btn_upload
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Upload File"
- onClicked: {
- file_dialog.open()
- }
- }
- FluProgressButton{
- id:btn_download
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Download File"
- onClicked: {
- folder_dialog.showDialog(function(path){
- FluNetwork.get("http://vjs.zencdn.net/v/oceans.mp4")
- .toDownload(path)
- .bind(root)
- .go(callable_download_file)
- })
- }
- }
- FluProgressButton{
- id:btn_download_breakpoint
- implicitWidth: parent.width
- implicitHeight: 36
- text: "Breakpoint Download File"
- onClicked: {
- folder_dialog.showDialog(function(path){
- FluNetwork.get("http://vjs.zencdn.net/v/oceans.mp4")
- .toDownload(path,true)
- .bind(root)
- .go(callable_breakpoint_download_file)
- })
- }
- }
- }
- }
-
- FluNetworkCallable{
- id:callable_upload_file
- onStart: {
- btn_upload.disabled = true
- }
- onFinish: {
- btn_upload.disabled = false
- }
- onError:
- (status,errorString,result)=>{
- btn_upload.progress = 0
- text_info.text = result
- console.debug(status+";"+errorString+";"+result)
- }
- onSuccess:
- (result)=>{
- text_info.text = result
- }
- onUploadProgress:
- (sent,total)=>{
- btn_upload.progress = sent/total
- }
- }
-
- FluNetworkCallable{
- id:callable_download_file
- onStart: {
- btn_download.progress = 0
- btn_download.disabled = true
- }
- onFinish: {
- btn_download.disabled = false
- }
- onError:
- (status,errorString,result)=>{
- btn_download.progress = 0
- showError(errorString)
- console.debug(status+";"+errorString+";"+result)
- }
- onSuccess:
- (result)=>{
- showSuccess(result)
- }
- onDownloadProgress:
- (recv,total)=>{
- btn_download.progress = recv/total
- }
- }
-
- FluNetworkCallable{
- id:callable_breakpoint_download_file
- onStart: {
- btn_download_breakpoint.progress = 0
- btn_download_breakpoint.disabled = true
- }
- onFinish: {
- btn_download_breakpoint.disabled = false
- }
- onError:
- (status,errorString,result)=>{
- btn_download_breakpoint.progress = 0
- showError(errorString)
- console.debug(status+";"+errorString+";"+result)
- }
- onSuccess:
- (result)=>{
- showSuccess(result)
- }
- onDownloadProgress:
- (recv,total)=>{
- btn_download_breakpoint.progress = recv/total
- }
- }
-
- FileDialog {
- id: file_dialog
- onAccepted: {
- FluNetwork.postForm("https://httpbingo.org/post")
- .setRetry(1)//只请求一次
- .add("accessToken","12345678")
- .addFile("file",FluTools.toLocalPath(file_dialog.currentFile))
- .bind(root)
- .go(callable_upload_file)
- }
- }
-
- FileDialog {
- property var onSelectListener
- id: folder_dialog
- folder: StandardPaths.standardLocations(StandardPaths.DownloadLocation)[0]
- currentFile: StandardPaths.standardLocations(StandardPaths.DownloadLocation)[0]+"/oceans.mp4"
- fileMode: FileDialog.SaveFile
- onAccepted: {
- folder_dialog.onSelectListener(FluTools.toLocalPath(folder_dialog.currentFile))
- }
- function showDialog(listener){
- folder_dialog.onSelectListener = listener
- folder_dialog.open()
- }
- }
-
- FluArea{
- anchors{
- top: layout_flick.top
- bottom: layout_flick.bottom
- left: layout_flick.right
- right: parent.right
- leftMargin: 8
- }
- Flickable{
- clip: true
- id:scrollview
- boundsBehavior:Flickable.StopAtBounds
- width: parent.width
- height: parent.height
- contentWidth: width
- contentHeight: text_info.height
- ScrollBar.vertical: FluScrollBar {}
- FluText{
- id:text_info
- width: scrollview.width
- wrapMode: Text.WrapAnywhere
- padding: 14
- }
- }
- }
-}
diff --git a/example/qml-Qt6/page/T_Pagination.qml b/example/qml-Qt6/page/T_Pagination.qml
deleted file mode 100644
index f2e85c11..00000000
--- a/example/qml-Qt6/page/T_Pagination.qml
+++ /dev/null
@@ -1,49 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Pagination")
-
- FluArea{
- Layout.fillWidth: true
- height: 200
- paddings: 10
- Layout.topMargin: 20
- ColumnLayout{
- spacing: 20
- anchors.verticalCenter: parent.verticalCenter
- FluPagination{
- pageCurrent: 1
- pageButtonCount: 5
- itemCount: 5000
- }
- FluPagination{
- pageCurrent: 2
- itemCount: 5000
- pageButtonCount: 7
- }
- FluPagination{
- pageCurrent: 3
- itemCount: 5000
- pageButtonCount: 9
- }
- }
-
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluPagination{
- pageCurrent: 1
- itemCount: 1000
- pageButtonCount: 9
-}'
- }
-
-
-}
diff --git a/example/qml-Qt6/page/T_Pivot.qml b/example/qml-Qt6/page/T_Pivot.qml
deleted file mode 100644
index b52a4928..00000000
--- a/example/qml-Qt6/page/T_Pivot.qml
+++ /dev/null
@@ -1,82 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Window
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Pivot")
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 400
- paddings: 10
-
-
-
- FluPivot{
- anchors.fill: parent
- currentIndex: 2
-
- FluPivotItem{
- title: qsTr("All")
- contentItem:FluText{
- text: qsTr("All emails go here.")
- }
- }
- FluPivotItem{
- title: qsTr("Unread")
- contentItem: FluText{
- text: qsTr("Unread emails go here.")
- }
- }
- FluPivotItem{
- title: qsTr("Flagged")
- contentItem: FluText{
- text: qsTr("Flagged emails go here.")
- }
- }
- FluPivotItem{
- title: qsTr("Urgent")
- contentItem: FluText{
- text: qsTr("Urgent emails go here.")
- }
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluPivot{
- anchors.fill: parent
- FluPivotItem:{
- text: qsTr("All")
- contentItem: FluText{
- text: qsTr("All emails go here.")
- }
- }
- FluPivotItem:{
- text: qsTr("Unread")
- contentItem: FluText{
- text: qsTr("Unread emails go here.")
- }
- }
- FluPivotItem:{
- text: qsTr("Flagged")
- contentItem: FluText{
- text: qsTr("Flagged emails go here.")
- }
- }
- FluPivotItem:{
- text: qsTr("Urgent")
- contentItem: FluText{
- text: qsTr("Urgent emails go here.")
- }
- }
-}
-'
- }
-}
diff --git a/example/qml-Qt6/page/T_Progress.qml b/example/qml-Qt6/page/T_Progress.qml
deleted file mode 100644
index ecbfb488..00000000
--- a/example/qml-Qt6/page/T_Progress.qml
+++ /dev/null
@@ -1,103 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Progress")
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 130
- paddings: 10
-
- ColumnLayout{
- spacing: 10
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- FluText{
- text: "indeterminate = true"
- }
- FluProgressBar{
- }
- FluProgressRing{
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluProgressBar{
-
-}
-FluProgressRing{
-
-}
-'
- }
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 286
- paddings: 10
-
- ColumnLayout{
- spacing: 10
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- FluText{
- text: "indeterminate = false"
- }
- FluProgressBar{
- indeterminate: false
- value:slider.value/100
- Layout.topMargin: 10
- }
- FluProgressBar{
- indeterminate: false
- value:slider.value/100
- progressVisible: true
- Layout.topMargin: 10
- }
- FluProgressRing{
- indeterminate: false
- value: slider.value/100
- Layout.topMargin: 10
- }
- FluProgressRing{
- progressVisible: true
- indeterminate: false
- value: slider.value/100
- }
- FluSlider{
- id:slider
- Component.onCompleted: {
- value = 50
- }
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluProgressBar{
- indeterminate: false
-}
-FluProgressRing{
- indeterminate: false
- progressVisible: true
-}
-'
- }
-
-
-}
diff --git a/example/qml-Qt6/page/T_QRCode.qml b/example/qml-Qt6/page/T_QRCode.qml
deleted file mode 100644
index 9ed3ced0..00000000
--- a/example/qml-Qt6/page/T_QRCode.qml
+++ /dev/null
@@ -1,102 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Window
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("QRCode")
-
- FluQRCode{
- id:qrcode
- Layout.topMargin: 20
- size:slider_size.value
- text:text_box.text
- color:color_picker.current
- bgColor: bgcolor_picker.current
- margins:slider_margins.value
- Layout.preferredWidth: size
- Layout.preferredHeight: size
- }
-
- RowLayout{
- spacing: 10
- Layout.topMargin: 20
- FluText{
- text:"text:"
- Layout.alignment: Qt.AlignVCenter
- }
- FluTextBox{
- id:text_box
- text:"会磨刀的小猪"
- Layout.preferredWidth: 240
- }
- }
-
- RowLayout{
- spacing: 10
- Layout.topMargin: 10
- FluText{
- text:"color:"
- Layout.alignment: Qt.AlignVCenter
- }
- FluColorPicker{
- id:color_picker
- current: Qt.rgba(0,0,0,1)
- }
- }
-
- RowLayout{
- spacing: 10
- Layout.topMargin: 10
- FluText{
- text:"bgColor:"
- Layout.alignment: Qt.AlignVCenter
- }
- FluColorPicker{
- id:bgcolor_picker
- current: Qt.rgba(1,1,1,1)
- }
- }
-
- RowLayout{
- spacing: 10
- FluText{
- text:"margins:"
- Layout.alignment: Qt.AlignVCenter
- }
- FluSlider{
- id:slider_margins
- from:0
- to:80
- value: 0
- }
- }
-
- RowLayout{
- spacing: 10
- FluText{
- text:"size:"
- Layout.alignment: Qt.AlignVCenter
- }
- FluSlider{
- id:slider_size
- from:120
- to:260
- value: 120
- }
- }
-
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: 20
- code:'FluQRCode{
- color:"red"
- text:"会磨刀的小猪"
- size:100
-}'
- }
-
-}
diff --git a/example/qml-Qt6/page/T_RadioButton.qml b/example/qml-Qt6/page/T_RadioButton.qml
deleted file mode 100644
index eae17351..00000000
--- a/example/qml-Qt6/page/T_RadioButton.qml
+++ /dev/null
@@ -1,101 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("RadioButton")
-
- FluArea{
- Layout.fillWidth: true
- height: 68
- paddings: 10
- Layout.topMargin: 20
- Row{
- spacing: 30
- anchors.verticalCenter: parent.verticalCenter
- FluRadioButton{
- disabled: radio_button_switch.checked
- }
- FluRadioButton{
- disabled: radio_button_switch.checked
- text: qsTr("Right")
- }
- FluRadioButton{
- disabled: radio_button_switch.checked
- text: qsTr("Left")
- textRight: false
- }
- }
- FluToggleSwitch{
- id: radio_button_switch
- anchors{
- right: parent.right
- verticalCenter: parent.verticalCenter
- }
- text: qsTr("Disabled")
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluRadioButton{
- text:"Text"
-}'
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 100
- paddings: 10
- Layout.topMargin: 20
- FluRadioButtons{
- spacing: 8
- anchors.verticalCenter: parent.verticalCenter
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- FluRadioButton{
- disabled: radio_button_switch2.checked
- text: qsTr("Radio Button_1")
- }
- FluRadioButton{
- disabled: radio_button_switch2.checked
- text: qsTr("Radio Button_2")
- }
- FluRadioButton{
- disabled: radio_button_switch2.checked
- text: qsTr("Radio Button_3")
- }
- }
- FluToggleSwitch{
- id: radio_button_switch2
- anchors{
- right: parent.right
- verticalCenter: parent.verticalCenter
- }
- text: qsTr("Disabled")
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluRadioButtons{
- spacing: 8
- FluRadioButton{
- text:"Radio Button_1"
- }
- FluRadioButton{
- text:"Radio Button_2"
- }
- FluRadioButton{
- text:"Radio Button_3"
- }
-}'
- }
-
-}
diff --git a/example/qml-Qt6/page/T_RatingControl.qml b/example/qml-Qt6/page/T_RatingControl.qml
deleted file mode 100644
index 3a20e844..00000000
--- a/example/qml-Qt6/page/T_RatingControl.qml
+++ /dev/null
@@ -1,35 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage {
-
- title: qsTr("RatingControl")
-
- FluArea {
- Layout.fillWidth: true
- height: 100
- paddings: 10
- Layout.topMargin: 20
-
- Column {
- spacing: 10
- anchors.verticalCenter: parent.verticalCenter
- FluRatingControl {}
- FluRatingControl {
- number: 10
- }
- }
- }
-
- CodeExpander {
- Layout.fillWidth: true
- Layout.topMargin: -1
- code: 'FluRatingControl{
-
-}'
- }
-}
diff --git a/example/qml-Qt6/page/T_Rectangle.qml b/example/qml-Qt6/page/T_Rectangle.qml
deleted file mode 100644
index 9426e66f..00000000
--- a/example/qml-Qt6/page/T_Rectangle.qml
+++ /dev/null
@@ -1,74 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Controls
-import QtQuick.Window
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Rectangle")
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 80
- paddings: 10
-
- Column{
- spacing: 15
- anchors{
- left: parent.left
- verticalCenter: parent.verticalCenter
- }
- RowLayout{
- Layout.topMargin: 20
- FluRectangle{
- width: 50
- height: 50
- color:"#0078d4"
- radius:[0,0,0,0]
- }
- FluRectangle{
- width: 50
- height: 50
- color:"#744da9"
- radius:[15,15,15,15]
- }
- FluRectangle{
- width: 50
- height: 50
- color:"#ffeb3b"
- radius:[15,0,0,0]
- }
- FluRectangle{
- width: 50
- height: 50
- color:"#f7630c"
- radius:[0,15,0,0]
- }
- FluRectangle{
- width: 50
- height: 50
- color:"#e71123"
- radius:[0,0,15,0]
- }
- FluRectangle{
- width: 50
- height: 50
- color:"#b4009e"
- radius:[0,0,0,15]
- }
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluRectangle{
- radius: [25,25,25,25]
- width: 50
- height: 50
-}'
- }
-}
diff --git a/example/qml-Qt6/page/T_RemoteLoader.qml b/example/qml-Qt6/page/T_RemoteLoader.qml
deleted file mode 100644
index 8a1d077e..00000000
--- a/example/qml-Qt6/page/T_RemoteLoader.qml
+++ /dev/null
@@ -1,14 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluPage{
- launchMode: FluPageType.SingleTop
- FluRemoteLoader{
- anchors.fill: parent
- source: "https://zhu-zichu.gitee.io/T_RemoteLoader.qml"
- }
-}
diff --git a/example/qml-Qt6/page/T_Settings.qml b/example/qml-Qt6/page/T_Settings.qml
deleted file mode 100644
index c9f9e8a9..00000000
--- a/example/qml-Qt6/page/T_Settings.qml
+++ /dev/null
@@ -1,205 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../global"
-import "../component"
-import "../viewmodel"
-
-FluScrollablePage{
-
- title: qsTr("Settings")
-
- SettingsViewModel{
- id:viewmodel_settings
- }
-
- FluEvent{
- id:event_checkupdate_finish
- name: "checkUpdateFinish"
- onTriggered: {
- btn_checkupdate.loading = false
- }
- }
-
- Component.onCompleted: {
- FluEventBus.registerEvent(event_checkupdate_finish)
- }
-
- Component.onDestruction: {
- FluEventBus.unRegisterEvent(event_checkupdate_finish)
- }
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 60
- paddings: 10
- Row{
- spacing: 20
- anchors.verticalCenter: parent.verticalCenter
- FluText{
- text: "%1 v%2".arg(qsTr("Current Version")).arg(AppInfo.version)
- font: FluTextStyle.Body
- anchors.verticalCenter: parent.verticalCenter
- }
- FluLoadingButton{
- id: btn_checkupdate
- text: qsTr("Check for Updates")
- anchors.verticalCenter: parent.verticalCenter
- onClicked: {
- loading = true
- FluEventBus.post("checkUpdate")
- }
- }
- }
- }
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 50
- paddings: 10
- FluCheckBox{
- text: qsTr("Use System AppBar")
- checked: FluApp.useSystemAppBar
- anchors.verticalCenter: parent.verticalCenter
- onClicked: {
- FluApp.useSystemAppBar = !FluApp.useSystemAppBar
- dialog_restart.open()
- }
- }
- }
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 50
- paddings: 10
- FluCheckBox{
- text:qsTr("Fits AppBar Windows")
- checked: window.fitsAppBarWindows
- anchors.verticalCenter: parent.verticalCenter
- onClicked: {
- window.fitsAppBarWindows = !window.fitsAppBarWindows
- }
- }
- }
-
- FluContentDialog{
- id: dialog_restart
- title: qsTr("Friendly Reminder")
- message: qsTr("This action requires a restart of the program to take effect, is it restarted?")
- buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
- negativeText: qsTr("Cancel")
- positiveText: qsTr("OK")
- onPositiveClicked: {
- FluApp.exit(931)
- }
- }
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 128
- paddings: 10
-
- ColumnLayout{
- spacing: 5
- anchors{
- top: parent.top
- left: parent.left
- }
- FluText{
- text: qsTr("Dark Mode")
- font: FluTextStyle.BodyStrong
- Layout.bottomMargin: 4
- }
- Repeater{
- model: [{title:qsTr("System"),mode:FluThemeType.System},{title:qsTr("Light"),mode:FluThemeType.Light},{title:qsTr("Dark"),mode:FluThemeType.Dark}]
- delegate: FluRadioButton{
- checked : FluTheme.darkMode === modelData.mode
- text:modelData.title
- clickListener:function(){
- FluTheme.darkMode = modelData.mode
- }
- }
- }
- }
- }
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 160
- paddings: 10
-
- ColumnLayout{
- spacing: 5
- anchors{
- top: parent.top
- left: parent.left
- }
- FluText{
- text:qsTr("Navigation View Display Mode")
- font: FluTextStyle.BodyStrong
- Layout.bottomMargin: 4
- }
- Repeater{
- model: [{title:qsTr("Open"),mode:FluNavigationViewType.Open},{title:qsTr("Compact"),mode:FluNavigationViewType.Compact},{title:qsTr("Minimal"),mode:FluNavigationViewType.Minimal},{title:qsTr("Auto"),mode:FluNavigationViewType.Auto}]
- delegate: FluRadioButton{
- checked : viewmodel_settings.displayMode===modelData.mode
- text:modelData.title
- clickListener:function(){
- viewmodel_settings.displayMode = modelData.mode
- }
- }
- }
- }
- }
-
- ListModel{
- id:model_language
- ListElement{
- name:"en"
- }
- ListElement{
- name:"zh"
- }
- }
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 80
- paddings: 10
-
- ColumnLayout{
- spacing: 10
- anchors{
- top: parent.top
- left: parent.left
- }
- FluText{
- text:qsTr("Language")
- font: FluTextStyle.BodyStrong
- Layout.bottomMargin: 4
- }
- Flow{
- spacing: 5
- Repeater{
- model: TranslateHelper.languages
- delegate: FluRadioButton{
- checked: TranslateHelper.current === modelData
- text:modelData
- clickListener:function(){
- TranslateHelper.current = modelData
- dialog_restart.open()
- }
- }
- }
- }
- }
- }
-}
diff --git a/example/qml-Qt6/page/T_ShortcutPicker.qml b/example/qml-Qt6/page/T_ShortcutPicker.qml
deleted file mode 100644
index b2f51448..00000000
--- a/example/qml-Qt6/page/T_ShortcutPicker.qml
+++ /dev/null
@@ -1,30 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Window
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("ShortcutPicker")
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 100
- paddings: 10
- FluShortcutPicker{
- anchors.verticalCenter: parent.verticalCenter
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluShortcutPicker{
-
-}'
- }
-
-}
-
diff --git a/example/qml-Qt6/page/T_Slider.qml b/example/qml-Qt6/page/T_Slider.qml
deleted file mode 100644
index 2a46b2c7..00000000
--- a/example/qml-Qt6/page/T_Slider.qml
+++ /dev/null
@@ -1,59 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Slider")
-
- FluArea{
- Layout.fillWidth: true
- Layout.preferredHeight: 200
- Layout.topMargin: 20
- paddings: 10
-
- Row{
- spacing: 30
- FluSlider{
- }
- FluSlider{
- orientation: Qt.Vertical
- anchors.verticalCenter: parent.verticalCenter
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluSlider{
- value:50
-}'
- }
-
-
- FluArea{
- Layout.fillWidth: true
- Layout.preferredHeight: 200
- Layout.topMargin: 20
- paddings: 10
- Row{
- spacing: 30
- FluRangeSlider{
- }
- FluRangeSlider{
- orientation: Qt.Vertical
- anchors.verticalCenter: parent.verticalCenter
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluRangeSlider{
- orientation: Qt.Vertical
-}'
- }
-}
diff --git a/example/qml-Qt6/page/T_SplitLayout.qml b/example/qml-Qt6/page/T_SplitLayout.qml
deleted file mode 100644
index 303d6181..00000000
--- a/example/qml-Qt6/page/T_SplitLayout.qml
+++ /dev/null
@@ -1,84 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluContentPage{
-
- title: qsTr("SplitLayout")
-
- RowLayout{
- id:layout_dropdown
- anchors{
- top: parent.top
- topMargin: 20
- }
- FluText{
- text:"orientation:"
- }
- FluDropDownButton{
- id:btn_orientation
- Layout.preferredWidth: 120
- text:"Horizontal"
- FluMenuItem{
- text:"Horizontal"
- onClicked: {
- btn_orientation.text = text
- split_layout.orientation = Qt.Horizontal
- }
- }
- FluMenuItem{
- text:"Vertical"
- onClicked: {
- btn_orientation.text = text
- split_layout.orientation = Qt.Vertical
- }
- }
- }
- }
- FluSplitLayout {
- id:split_layout
- anchors{
- top: layout_dropdown.bottom
- left: parent.left
- right: parent.right
- bottom: parent.bottom
- topMargin: 8
- }
- orientation: Qt.Horizontal
- Item {
- clip: true
- implicitWidth: 200
- implicitHeight: 200
- SplitView.maximumWidth: 400
- SplitView.maximumHeight: 400
- FluText {
- text: "Page 1"
- anchors.centerIn: parent
- }
- }
- Item {
- clip: true
- id: centerItem
- SplitView.minimumWidth: 50
- SplitView.minimumHeight: 50
- SplitView.fillWidth: true
- SplitView.fillHeight: true
- FluText {
- text: "Page 2"
- anchors.centerIn: parent
- }
- }
- Item {
- clip: true
- implicitWidth: 200
- implicitHeight: 200
- FluText {
- text: "Page 3"
- anchors.centerIn: parent
- }
- }
- }
-}
diff --git a/example/qml-Qt6/page/T_StaggeredLayout.qml b/example/qml-Qt6/page/T_StaggeredLayout.qml
deleted file mode 100644
index 4594f9c6..00000000
--- a/example/qml-Qt6/page/T_StaggeredLayout.qml
+++ /dev/null
@@ -1,58 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI 1.0
-import "../component"
-
-FluContentPage{
-
- title: qsTr("StaggeredLayout")
-
- property var colors : [FluColors.Yellow,FluColors.Orange,FluColors.Red,FluColors.Magenta,FluColors.Purple,FluColors.Blue,FluColors.Teal,FluColors.Green]
-
- ListModel{
- id:list_model
- Component.onCompleted: {
- for(var i=0;i<=100;i++){
- var item = {}
- item.color = colors[rand(0,7)]
- item.height = rand(100,300)
- append(item)
- }
- }
-
- }
-
- Flickable{
- id: scroll
- anchors.fill: parent
- anchors.topMargin: 20
- boundsBehavior:Flickable.StopAtBounds
- contentHeight: staggered_view.implicitHeight
- clip: true
- ScrollBar.vertical: FluScrollBar {}
- FluStaggeredLayout{
- id:staggered_view
- width: parent.width
- itemWidth: 160
- model:list_model
- delegate: Rectangle{
- height: model.height
- color:model.color.normal
- FluText{
- color:"#FFFFFF"
- text:model.index
- font.bold: true
- font.pixelSize: 18
- anchors.centerIn: parent
- }
- }
- }
- }
-
- function rand(minNum, maxNum){
- return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
- }
-
-}
diff --git a/example/qml-Qt6/page/T_StatusLayout.qml b/example/qml-Qt6/page/T_StatusLayout.qml
deleted file mode 100644
index a2742461..00000000
--- a/example/qml-Qt6/page/T_StatusLayout.qml
+++ /dev/null
@@ -1,90 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Controls
-import QtQuick.Window
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("StatusLayout")
-
- FluArea{
- id:layout_actions
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 50
- paddings: 10
- RowLayout{
- spacing: 14
- FluDropDownButton{
- id:btn_status_mode
- Layout.preferredWidth: 140
- text:"Loading"
- FluMenuItem{
- text:"Loading"
- onClicked: {
- btn_status_mode.text = text
- status_view.statusMode = FluStatusLayoutType.Loading
- }
- }
- FluMenuItem{
- text:"Empty"
- onClicked: {
- btn_status_mode.text = text
- status_view.statusMode = FluStatusLayoutType.Empty
- }
- }
- FluMenuItem{
- text:"Error"
- onClicked: {
- btn_status_mode.text = text
- status_view.statusMode = FluStatusLayoutType.Error
- }
- }
- FluMenuItem{
- text:"Success"
- onClicked: {
- btn_status_mode.text = text
- status_view.statusMode = FluStatusLayoutType.Success
- }
- }
- }
- }
- }
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 10
- height: 380
- paddings: 10
- FluStatusLayout{
- id:status_view
- anchors.fill: parent
- loadingText: qsTr("Loading...")
- emptyText: qsTr("Empty")
- errorText: qsTr("The page went wrong...")
- errorButtonText: qsTr("Reload")
- onErrorClicked:{
- showError("Click Reload")
- }
- Rectangle {
- anchors.fill: parent
- color:FluTheme.primaryColor
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluStatusLayout{
- anchors.fill: parent
- statusMode: FluStatusLayoutType.Loading
- Rectangle{
- anchors.fill: parent
- color:FluTheme.primaryColor
- }
-}'
- }
-
-}
diff --git a/example/qml-Qt6/page/T_TabView.qml b/example/qml-Qt6/page/T_TabView.qml
deleted file mode 100644
index 76bed115..00000000
--- a/example/qml-Qt6/page/T_TabView.qml
+++ /dev/null
@@ -1,129 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Window
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- property var colors : [FluColors.Yellow,FluColors.Orange,FluColors.Red,FluColors.Magenta,FluColors.Purple,FluColors.Blue,FluColors.Teal,FluColors.Green]
-
- title: qsTr("TabView")
-
- Component{
- id:com_page
- Rectangle{
- anchors.fill: parent
- color: argument.normal
- }
- }
-
- function newTab(){
- tab_view.appendTab("qrc:/example/res/image/favicon.ico",qsTr("Document ")+tab_view.count(),com_page,colors[Math.floor(Math.random() * 8)])
- }
-
- Component.onCompleted: {
- newTab()
- newTab()
- newTab()
- }
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 50
- paddings: 10
- RowLayout{
- spacing: 14
- FluDropDownButton{
- id:btn_tab_width_behavior
- Layout.preferredWidth: 140
- text:"Equal"
- FluMenuItem{
- text:"Equal"
- onClicked: {
- btn_tab_width_behavior.text = text
- tab_view.tabWidthBehavior = FluTabViewType.Equal
- }
- }
- FluMenuItem{
- text:"SizeToContent"
- onClicked: {
- btn_tab_width_behavior.text = text
- tab_view.tabWidthBehavior = FluTabViewType.SizeToContent
- }
- }
- FluMenuItem{
- text:"Compact"
- onClicked: {
- btn_tab_width_behavior.text = text
- tab_view.tabWidthBehavior = FluTabViewType.Compact
- }
- }
- }
- FluDropDownButton{
- id:btn_close_button_visibility
- text:"Always"
- Layout.preferredWidth: 120
- FluMenuItem{
- text:"Never"
- onClicked: {
- btn_close_button_visibility.text = text
- tab_view.closeButtonVisibility = FluTabViewType.Never
- }
- }
- FluMenuItem{
- text:"Always"
- onClicked: {
- btn_close_button_visibility.text = text
- tab_view.closeButtonVisibility = FluTabViewType.Always
- }
- }
- FluMenuItem{
- text:"OnHover"
- onClicked: {
- btn_close_button_visibility.text = text
- tab_view.closeButtonVisibility = FluTabViewType.OnHover
- }
- }
- }
- }
- }
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 15
- height: 400
- paddings: 10
- FluTabView{
- id:tab_view
- onNewPressed:{
- newTab()
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluTabView{
- anchors.fill: parent
- Component.onCompleted: {
- newTab()
- newTab()
- newTab()
- }
- Component{
- id:com_page
- Rectangle{
- anchors.fill: parent
- color: argument
- }
- }
- function newTab(){
- tab_view.appendTab("qrc:/example/res/image/favicon.ico","Document 1",com_page,argument)
- }
-}
-'
- }
-}
diff --git a/example/qml-Qt6/page/T_TableView.qml b/example/qml-Qt6/page/T_TableView.qml
deleted file mode 100644
index 00b1c4d3..00000000
--- a/example/qml-Qt6/page/T_TableView.qml
+++ /dev/null
@@ -1,589 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Window
-import FluentUI
-import "../component"
-
-FluContentPage{
-
- id:root
- title: qsTr("TableView")
- signal checkBoxChanged
-
- property var dataSource : []
- property int sortType: 0
- property bool seletedAll: true
- property string nameKeyword: ""
-
- onNameKeywordChanged: {
- table_view.filter(function(item){
- if(item.name.includes(nameKeyword)){
- return true
- }
- return false
- })
- }
-
- Component.onCompleted: {
- loadData(1,1000)
- }
-
- onCheckBoxChanged: {
- for(var i =0;i< table_view.rows ;i++){
- if(false === table_view.getRow(i).checkbox.options.checked){
- root.seletedAll = false
- return
- }
- }
- root.seletedAll = true
- }
-
- onSortTypeChanged: {
- table_view.closeEditor()
- if(sortType === 0){
- table_view.sort()
- }else if(sortType === 1){
- table_view.sort(
- (l, r) =>{
- var lage = Number(l.age)
- var rage = Number(r.age)
- if(lage === rage){
- return l._key>r._key
- }
- return lage>rage
- });
- }else if(sortType === 2){
- table_view.sort(
- (l, r) => {
- var lage = Number(l.age)
- var rage = Number(r.age)
- if(lage === rage){
- return l._key>r._key
- }
- return lage element === display)
- selectAll()
- }
- onCommit: {
- editTextChaged(editText)
- tableView.closeEditor()
- }
- }
- }
-
- Component{
- id:com_avatar
- Item{
- FluClip{
- anchors.centerIn: parent
- width: 40
- height: 40
- radius: [20,20,20,20]
- Image{
- anchors.fill: parent
- source: {
- if(options && options.avatar){
- return options.avatar
- }
- return ""
- }
- sourceSize: Qt.size(80,80)
- }
- }
- }
- }
-
- Component{
- id:com_column_update_title
- Item{
- FluText{
- id:text_title
- text: {
- if(options.title){
- return options.title
- }
- return ""
- }
- anchors.fill: parent
- verticalAlignment: Qt.AlignVCenter
- horizontalAlignment: Qt.AlignHCenter
- elide: Text.ElideRight
- }
- MouseArea{
- anchors.fill: parent
- cursorShape: Qt.PointingHandCursor
- onClicked: {
- custom_update_dialog.showDialog(options.title,function(text){
- itemModel.display = table_view.customItem(com_column_update_title,{"title":text})
- })
- }
- }
- }
- }
-
- Component{
- id:com_column_sort_age
- Item{
- FluText{
- text: qsTr("Age")
- anchors.centerIn: parent
- }
- ColumnLayout{
- spacing: 0
- anchors{
- right: parent.right
- verticalCenter: parent.verticalCenter
- rightMargin: 4
- }
- FluIconButton{
- Layout.preferredWidth: 20
- Layout.preferredHeight: 15
- iconSize: 12
- verticalPadding:0
- horizontalPadding:0
- iconSource: FluentIcons.ChevronUp
- iconColor: {
- if(1 === root.sortType){
- return FluTheme.primaryColor
- }
- return FluTheme.dark ? Qt.rgba(1,1,1,1) : Qt.rgba(0,0,0,1)
- }
- onClicked: {
- if(root.sortType === 1){
- root.sortType = 0
- return
- }
- root.sortType = 1
- }
- }
- FluIconButton{
- Layout.preferredWidth: 20
- Layout.preferredHeight: 15
- iconSize: 12
- verticalPadding:0
- horizontalPadding:0
- iconSource: FluentIcons.ChevronDown
- iconColor: {
- if(2 === root.sortType){
- return FluTheme.primaryColor
- }
- return FluTheme.dark ? Qt.rgba(1,1,1,1) : Qt.rgba(0,0,0,1)
- }
- onClicked: {
- if(root.sortType === 2){
- root.sortType = 0
- return
- }
- root.sortType = 2
- }
- }
- }
- }
- }
-
- FluArea{
- id:layout_controls
- anchors{
- left: parent.left
- right: parent.right
- top: parent.top
- topMargin: 20
- }
- height: 60
-
- Row{
- spacing: 5
- anchors{
- left: parent.left
- leftMargin: 10
- verticalCenter: parent.verticalCenter
- }
-
- FluButton{
- text: qsTr("Clear All")
- onClicked: {
- table_view.dataSource = []
- }
- }
-
- FluButton{
- text: qsTr("Delete Selection")
- onClicked: {
- var data = []
- var rows = []
- for (var i = 0; i < table_view.rows; i++) {
- var item = table_view.getRow(i);
- rows.push(item)
- if (!item.checkbox.options.checked) {
- data.push(item);
- }
- }
- var sourceModel = table_view.sourceModel;
- for (i = 0; i < sourceModel.rowCount; i++) {
- var sourceItem = sourceModel.getRow(i);
- const foundItem = rows.find(item=> item._key === sourceItem._key)
- if (!foundItem) {
- data.push(sourceItem);
- }
- }
- table_view.dataSource = data
- }
- }
-
- FluButton{
- text: qsTr("Add a row of Data")
- onClicked: {
- table_view.appendRow(genTestObject())
- }
- }
-
- }
- }
-
- FluTableView{
- id:table_view
- anchors{
- left: parent.left
- right: parent.right
- top: layout_controls.bottom
- bottom: gagination.top
- }
- anchors.topMargin: 5
- onRowsChanged: {
- root.checkBoxChanged()
- }
- columnSource:[
- {
- title: table_view.customItem(com_column_checbox,{checked:true}),
- dataIndex: 'checkbox',
- width:100,
- minimumWidth:100,
- maximumWidth:100
- },
- {
- title: table_view.customItem(com_column_update_title,{title:qsTr("Avatar")}),
- dataIndex: 'avatar',
- width:100
- },
- {
- title: table_view.customItem(com_column_filter_name,{title:qsTr("Name")}),
- dataIndex: 'name',
- readOnly:true
- },
- {
- title: table_view.customItem(com_column_sort_age,{sort:0}),
- dataIndex: 'age',
- editDelegate:com_combobox,
- width:100,
- minimumWidth:100,
- maximumWidth:100
- },
- {
- title: qsTr("Address"),
- dataIndex: 'address',
- width:200,
- minimumWidth:100,
- maximumWidth:250
- },
- {
- title: qsTr("Nickname"),
- dataIndex: 'nickname',
- width:100,
- minimumWidth:80,
- maximumWidth:200
- },
- {
- title: qsTr("Long String"),
- dataIndex: 'longstring',
- width:200,
- minimumWidth:100,
- maximumWidth:300
- },
- {
- title: qsTr("Options"),
- dataIndex: 'action',
- width:160,
- minimumWidth:160,
- maximumWidth:160
- }
- ]
- }
-
- FluPagination{
- id:gagination
- anchors{
- bottom: parent.bottom
- left: parent.left
- }
- pageCurrent: 1
- itemCount: 100000
- pageButtonCount: 7
- __itemPerPage: 1000
- previousText: qsTr("")
- onRequestPage:
- (page,count)=> {
- table_view.closeEditor()
- loadData(page,count)
- table_view.resetPosition()
- }
- }
-
- function genTestObject(){
- var ages = ["100", "300", "500", "1000"];
- function getRandomAge() {
- var randomIndex = Math.floor(Math.random() * ages.length);
- return ages[randomIndex];
- }
- var names = ["孙悟空", "猪八戒", "沙和尚", "唐僧","白骨夫人","金角大王","熊山君","黄风怪","银角大王"];
- function getRandomName(){
- var randomIndex = Math.floor(Math.random() * names.length);
- return names[randomIndex];
- }
- var nicknames = ["复海大圣","混天大圣","移山大圣","通风大圣","驱神大圣","齐天大圣","平天大圣"]
- function getRandomNickname(){
- var randomIndex = Math.floor(Math.random() * nicknames.length);
- return nicknames[randomIndex];
- }
- var addresses = ["傲来国界花果山水帘洞","傲来国界坎源山脏水洞","大唐国界黑风山黑风洞","大唐国界黄风岭黄风洞","大唐国界骷髅山白骨洞","宝象国界碗子山波月洞","宝象国界平顶山莲花洞","宝象国界压龙山压龙洞","乌鸡国界号山枯松涧火云洞","乌鸡国界衡阳峪黑水河河神府"]
- function getRandomAddresses(){
- var randomIndex = Math.floor(Math.random() * addresses.length);
- return addresses[randomIndex];
- }
- var avatars = ["qrc:/example/res/svg/avatar_1.svg", "qrc:/example/res/svg/avatar_2.svg", "qrc:/example/res/svg/avatar_3.svg", "qrc:/example/res/svg/avatar_4.svg","qrc:/example/res/svg/avatar_5.svg","qrc:/example/res/svg/avatar_6.svg","qrc:/example/res/svg/avatar_7.svg","qrc:/example/res/svg/avatar_8.svg","qrc:/example/res/svg/avatar_9.svg","qrc:/example/res/svg/avatar_10.svg","qrc:/example/res/svg/avatar_11.svg","qrc:/example/res/svg/avatar_12.svg"];
- function getAvatar(){
- var randomIndex = Math.floor(Math.random() * avatars.length);
- return avatars[randomIndex];
- }
- return {
- checkbox: table_view.customItem(com_checbox,{checked:root.seletedAll}),
- avatar:table_view.customItem(com_avatar,{avatar:getAvatar()}),
- name: getRandomName(),
- age:getRandomAge(),
- address: getRandomAddresses(),
- nickname: getRandomNickname(),
- longstring:"你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好",
- action: table_view.customItem(com_action),
- _minimumHeight:50,
- _key:FluTools.uuid()
- }
- }
- function loadData(page,count){
- root.seletedAll = true
- const dataSource = []
- for(var i=0;i {
- Qt.openUrlExternally(link)
- }
- onLinkHovered:
- (link)=> {
- if(link === ""){
- FluTools.restoreOverrideCursor()
- }else{
- FluTools.setOverrideCursor(Qt.PointingHandCursor)
- }
- }
- }
- }
-
- ListModel{
- id:list_model
- ListElement{
- lable:"2013-09-01"
- text:' 考上中国皮城大学,杰斯武器工坊专业'
- }
- ListElement{
- lable:"2017-07-01"
- text:"大学毕业,在寝室打了4年LOL,没想到毕业还要找工作,瞬间蒙蔽,害"
- }
- ListElement{
- lable:"2017-09-01"
- text:"开始找工作,毕业即失业!回农村老家躺平,继承三亩良田"
- }
- ListElement{
- lable:"2018-02-01"
- text:"玩了一年没钱,惨,出去找工作上班"
- }
- ListElement{
- lable:"2018-03-01"
- text:"找到一份Android外包开发岗位,开发了一个Android应用,满满成就感!前端、服务端、Flutter也都懂一丢丢,什么都会什么都不精通,钱途无望"
- }
- ListElement{
- lable:"2021-06-01"
- text:"由于某个项目紧急,临时加入Qt项目组(就因为大学学了点C++),本来是想进去打个酱油,到后面竟然成开发主力,坑啊"
- }
- ListElement{
- lable:"2022-08-01"
- text:"额,被老板卖到甲方公司,走时老板还问我想不想去,我说:'哪里工资高就去哪里',老板:'无语'"
- }
- ListElement{
- lable:"2023-02-28"
- text:"开发FluentUI组件库"
- }
- ListElement{
- lable:"2023-03-28"
- text:'将FluentUI源码开源到github,并发布视频到B站'
- lableDelegate:()=>com_lable
- textDelegate:()=>com_text
- dot:()=>com_dot
- }
- }
-
- RowLayout{
- spacing: 20
- Layout.topMargin: 20
- FluTextBox{
- id: text_box
- text: "Technical testing 2015-09-01"
- Layout.preferredWidth: 240
- }
- FluFilledButton{
- text: qsTr("Append")
- onClicked: {
- list_model.append({text:text_box.text})
- }
- }
- FluFilledButton{
- text: qsTr("clear")
- onClicked: {
- list_model.clear()
- }
- }
- }
-
- RowLayout{
- Layout.topMargin: 10
- FluText{
- text:"mode:"
- }
- FluDropDownButton{
- id: btn_mode
- Layout.preferredWidth: 100
- text: "Alternate"
- FluMenuItem{
- text: "Left"
- onClicked: {
- btn_mode.text = text
- time_line.mode = FluTimelineType.Left
- }
- }
- FluMenuItem{
- text: "Right"
- onClicked: {
- btn_mode.text = text
- time_line.mode = FluTimelineType.Right
- }
- }
- FluMenuItem{
- text: "Alternate"
- onClicked: {
- btn_mode.text = text
- time_line.mode = FluTimelineType.Alternate
- }
- }
- }
- }
-
- FluTimeline{
- id: time_line
- Layout.fillWidth: true
- Layout.topMargin: 20
- Layout.bottomMargin: 20
- mode: FluTimelineType.Alternate
- model: list_model
- }
-
-}
diff --git a/example/qml-Qt6/page/T_ToggleSwitch.qml b/example/qml-Qt6/page/T_ToggleSwitch.qml
deleted file mode 100644
index 9220ff9f..00000000
--- a/example/qml-Qt6/page/T_ToggleSwitch.qml
+++ /dev/null
@@ -1,51 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("ToggleSwitch")
-
- FluArea{
- Layout.fillWidth: true
- height: 68
- paddings: 10
- Layout.topMargin: 20
- Row{
- spacing: 30
- anchors.verticalCenter: parent.verticalCenter
- FluToggleSwitch{
- disabled: toggle_switch.checked
- }
- FluToggleSwitch{
- disabled: toggle_switch.checked
- text: qsTr("Right")
- }
- FluToggleSwitch{
- disabled: toggle_switch.checked
- text: qsTr("Left")
- textRight: false
- }
- }
- FluToggleSwitch{
- id: toggle_switch
- anchors{
- right: parent.right
- verticalCenter: parent.verticalCenter
- }
- text: qsTr("Disabled")
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluToggleSwitch{
- text:"Text"
-}'
- }
-
-
-}
diff --git a/example/qml-Qt6/page/T_Tooltip.qml b/example/qml-Qt6/page/T_Tooltip.qml
deleted file mode 100644
index 4d1b8ed8..00000000
--- a/example/qml-Qt6/page/T_Tooltip.qml
+++ /dev/null
@@ -1,103 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Window
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Tooltip")
-
- FluText{
- Layout.topMargin: 20
- text: qsTr("Hover over Tultip and it pops up")
- }
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 68
- paddings: 10
-
- Column{
- spacing: 5
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- FluText{
- text: qsTr("Text properties of FluIconButton support the Tooltip pop-up window by default")
- }
- FluIconButton{
- iconSource:FluentIcons.ChromeCloseContrast
- iconSize: 15
- text: qsTr("Delete")
- onClicked:{
- showSuccess(qsTr("Click IconButton"))
- }
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluIconButton{
- iconSource:FluentIcons.ChromeCloseContrast
- iconSize: 15
- text: qsTr("Delete")
- onClicked:{
- showSuccess(qsTr("Click IconButton"))
- }
-}
-'
- }
-
- FluArea{
- Layout.fillWidth: true
- Layout.topMargin: 20
- height: 68
- paddings: 10
-
- Column{
- spacing: 5
- anchors{
- verticalCenter: parent.verticalCenter
- left: parent.left
- }
- FluText{
- text: qsTr("Add a Tooltip pop-up to a Button")
- }
- FluButton{
- id:button_1
- text: qsTr("Delete")
- onClicked:{
- showSuccess(qsTr("Click Button"))
- }
- FluTooltip{
- visible: button_1.hovered
- text:button_1.text
- delay: 1000
- }
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluButton{
- id: button_1
- text: qsTr("Delete")
- FluTooltip{
- visible: button_1.hovered
- text:button_1.text
- delay: 1000
- }
- onClicked:{
- showSuccess(qsTr("Click Button"))
- }
-}'
- }
-
-
-}
diff --git a/example/qml-Qt6/page/T_Tour.qml b/example/qml-Qt6/page/T_Tour.qml
deleted file mode 100644
index 830f58c5..00000000
--- a/example/qml-Qt6/page/T_Tour.qml
+++ /dev/null
@@ -1,80 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluScrollablePage{
-
- title: qsTr("Tour")
-
- FluTour{
- id:tour
- steps:[
- {title:qsTr("Upload File"),description: qsTr("Put your files here."),target:()=>btn_upload},
- {title:qsTr("Save"),description: qsTr("Save your changes."),target:()=>btn_save},
- {title:qsTr("Other Actions"),description: qsTr("Click to see other actions."),target:()=>btn_more}
- ]
- }
-
- FluArea{
- Layout.fillWidth: true
- height: 130
- paddings: 10
- Layout.topMargin: 20
-
- FluFilledButton{
- anchors{
- top: parent.top
- topMargin: 14
- }
- text: qsTr("Begin Tour")
- onClicked: {
- tour.open()
- }
- }
-
- Row{
- spacing: 20
- anchors{
- top: parent.top
- topMargin: 60
- }
- FluButton{
- id: btn_upload
- text: qsTr("Upload")
- onClicked: {
- showInfo(qsTr("Upload"))
- }
- }
- FluFilledButton{
- id: btn_save
- text: qsTr("Save")
- onClicked: {
- showInfo(qsTr("Save"))
- }
- }
- FluIconButton{
- id: btn_more
- iconSource: FluentIcons.More
- onClicked: {
- showInfo(qsTr("More"))
- }
- }
- }
- }
- CodeExpander{
- Layout.fillWidth: true
- Layout.topMargin: -1
- code:'FluTour{
- id:tour
- steps:[
- {title:"Upload File",description: "Put your files here.",target:()=>btn_upload},
- {title:"Save",description: "Save your changes.",target:()=>btn_save},
- {title:"Other Actions",description: "Click to see other actions.",target:()=>btn_more}
- ]
-}'
- }
-
-}
diff --git a/example/qml-Qt6/page/T_TreeView.qml b/example/qml-Qt6/page/T_TreeView.qml
deleted file mode 100644
index 02e09ac8..00000000
--- a/example/qml-Qt6/page/T_TreeView.qml
+++ /dev/null
@@ -1,131 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Window
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluContentPage {
-
- title: qsTr("TreeView")
-
- function treeData(){
- const dig = (path = '0', level = 4) => {
- const list = [];
- for (let i = 0; i < 6; i += 1) {
- const key = `${path}-${i}`;
- const treeNode = {
- title: key,
- key,
- };
- if (level > 0) {
- treeNode.children = dig(key, level - 1);
- }
- list.push(treeNode);
- }
- return list;
- };
- return dig();
- }
-
- Column{
- id: layout_column
- spacing: 12
- width: 300
- anchors{
- topMargin: 20
- top:parent.top
- left: parent.left
- leftMargin: 10
- bottom:parent.bottom
- bottomMargin: 20
- }
-
- FluText{
- text: qsTr("Total %1 data, %2 data currently displayed").arg(tree_view.count()).arg(tree_view.visibleCount())
- }
-
- FluText{
- text: qsTr("A total of %1 data items are selected").arg(tree_view.selectionModel().length)
- }
-
- RowLayout{
- spacing: 10
- FluText{
- text: "cellHeight:"
- Layout.alignment: Qt.AlignVCenter
- }
- FluSlider{
- id: slider_cell_height
- value: 30
- from: 30
- to:100
- }
- }
- RowLayout{
- spacing: 10
- FluText{
- text: "depthPadding:"
- Layout.alignment: Qt.AlignVCenter
- }
- FluSlider{
- id: slider_depth_padding
- value: 30
- from: 30
- to:100
- }
- }
- FluToggleSwitch{
- id: switch_showline
- text:"showLine"
- checked: false
- }
- FluToggleSwitch{
- id: switch_draggable
- text:"draggable"
- checked: false
- }
- FluToggleSwitch{
- id: switch_checkable
- text:"checkable"
- checked: false
- }
- FluButton{
- text: "all expand"
- onClicked: {
- tree_view.allExpand()
- }
- }
- FluButton{
- text: "all collapse"
- onClicked: {
- tree_view.allCollapse()
- }
- }
- }
- FluArea{
- anchors{
- left: layout_column.right
- top: parent.top
- bottom: parent.bottom
- right: parent.right
- rightMargin: 5
- topMargin: 5
- bottomMargin: 5
- }
- FluShadow{}
- FluTreeView{
- id:tree_view
- anchors.fill: parent
- cellHeight: slider_cell_height.value
- draggable:switch_draggable.checked
- showLine: switch_showline.checked
- checkable:switch_checkable.checked
- depthPadding: slider_depth_padding.value
- Component.onCompleted: {
- var data = treeData()
- dataSource = data
- }
- }
- }
-}
diff --git a/example/qml-Qt6/page/T_Typography.qml b/example/qml-Qt6/page/T_Typography.qml
deleted file mode 100644
index 7d3fd445..00000000
--- a/example/qml-Qt6/page/T_Typography.qml
+++ /dev/null
@@ -1,84 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Controls
-import FluentUI
-
-FluContentPage {
-
- property real textScale: 1
-
- title: qsTr("Typography")
- rightPadding: 10
-
- FluArea{
- anchors{
- top:parent.top
- left: parent.left
- right: parent.right
- bottom: parent.bottom
- topMargin: 20
- }
- paddings: 10
- ColumnLayout{
- spacing: 0
- scale: textScale
- transformOrigin: Item.TopLeft
- FluText{
- id:text_Display
- text:"Display"
- padding: 0
- font: FluTextStyle.Display
- }
- FluText{
- id:text_TitleLarge
- text:"Title Large"
- padding: 0
- font: FluTextStyle.TitleLarge
- }
- FluText{
- id:text_Title
- text:"Title"
- padding: 0
- font: FluTextStyle.Title
- }
- FluText{
- id:text_Subtitle
- text:"Subtitle"
- padding: 0
- font: FluTextStyle.Subtitle
- }
- FluText{
- id:text_BodyStrong
- text:"Body Strong"
- padding: 0
- font: FluTextStyle.BodyStrong
- }
- FluText{
- id:text_Body
- text:"Body"
- padding: 0
- font: FluTextStyle.Body
- }
- FluText{
- id:text_Caption
- text:"Caption"
- padding: 0
- font: FluTextStyle.Caption
- }
- }
-
- FluSlider{
- id:slider
- orientation: Qt.Vertical
- anchors{
- right: parent.right
- rightMargin: 45
- top: parent.top
- topMargin: 30
- }
- onValueChanged:{
- textScale = 1+value/100
- }
- }
- }
-}
diff --git a/example/qml-Qt6/page/T_Watermark.qml b/example/qml-Qt6/page/T_Watermark.qml
deleted file mode 100644
index a383b4a7..00000000
--- a/example/qml-Qt6/page/T_Watermark.qml
+++ /dev/null
@@ -1,131 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Window
-import FluentUI
-import "../component"
-
-FluContentPage{
-
- title: qsTr("Watermark")
-
- FluArea{
- anchors.fill: parent
- anchors.topMargin: 20
-
- ColumnLayout{
- anchors{
- left: parent.left
- leftMargin: 14
- }
-
- RowLayout{
- spacing: 10
- Layout.topMargin: 14
- FluText{
- text: "text:"
- Layout.alignment: Qt.AlignVCenter
- }
- FluTextBox{
- id: text_box
- text: "会磨刀的小猪"
- Layout.preferredWidth: 240
- }
- }
-
- RowLayout{
- spacing: 10
- FluText{
- text: "textSize:"
- Layout.alignment: Qt.AlignVCenter
- }
- FluSlider{
- id: slider_text_size
- value: 20
- from: 13
- to:50
- }
- }
- RowLayout{
- spacing: 10
- FluText{
- text: "gapX:"
- Layout.alignment: Qt.AlignVCenter
- }
- FluSlider{
- id:slider_gap_x
- value: 100
- }
- }
- RowLayout{
- spacing: 10
- FluText{
- text: "gapY:"
- Layout.alignment: Qt.AlignVCenter
- }
- FluSlider{
- id: slider_gap_y
- value: 100
- }
- }
- RowLayout{
- spacing: 10
- FluText{
- text: "offsetX:"
- Layout.alignment: Qt.AlignVCenter
- }
- FluSlider{
- id:slider_offset_x
- value: 50
- }
- }
- RowLayout{
- spacing: 10
- FluText{
- text: "offsetY:"
- Layout.alignment: Qt.AlignVCenter
- }
- FluSlider{
- id: slider_offset_y
- value: 50
- }
- }
- RowLayout{
- spacing: 10
- FluText{
- text: "rotate:"
- Layout.alignment: Qt.AlignVCenter
- }
- FluSlider{
- id: slider_rotate
- value: 22
- from: 0
- to:360
- }
- }
- RowLayout{
- spacing: 10
- FluText{
- text: "textColor:"
- Layout.alignment: Qt.AlignVCenter
- }
- FluColorPicker{
- id: color_picker
- current: Qt.rgba(0,0,0,0.1)
- }
- }
- }
-
- FluWatermark{
- id: water_mark
- anchors.fill: parent
- text:text_box.text
- textColor: color_picker.current
- textSize: slider_text_size.value
- rotate: slider_rotate.value
- gap:Qt.point(slider_gap_x.value,slider_gap_y.value)
- offset: Qt.point(slider_offset_x.value,slider_offset_y.value)
- }
- }
-
-}
diff --git a/example/qml-Qt6/viewmodel/SettingsViewModel.qml b/example/qml-Qt6/viewmodel/SettingsViewModel.qml
deleted file mode 100644
index 6a8bf031..00000000
--- a/example/qml-Qt6/viewmodel/SettingsViewModel.qml
+++ /dev/null
@@ -1,14 +0,0 @@
-import QtQuick
-import FluentUI
-
-FluViewModel{
-
- objectName: "SettingsViewModel"
- scope: FluViewModelType.Application
- property int displayMode
-
- onInitData: {
- displayMode = FluNavigationViewType.Auto
- }
-
-}
diff --git a/example/qml-Qt6/viewmodel/TextBoxViewModel.qml b/example/qml-Qt6/viewmodel/TextBoxViewModel.qml
deleted file mode 100644
index e06ae30f..00000000
--- a/example/qml-Qt6/viewmodel/TextBoxViewModel.qml
+++ /dev/null
@@ -1,8 +0,0 @@
-import QtQuick
-import FluentUI
-
-FluViewModel {
- objectName: "TextBoxView"
- property string text1
- property string text2
-}
diff --git a/example/qml-Qt6/window/AboutWindow.qml b/example/qml-Qt6/window/AboutWindow.qml
deleted file mode 100644
index 2cd6166b..00000000
--- a/example/qml-Qt6/window/AboutWindow.qml
+++ /dev/null
@@ -1,165 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import FluentUI
-import "../component"
-
-FluWindow {
-
- id:window
- title:"关于"
- width: 600
- height: 580
- fixSize: true
- launchMode: FluWindowType.SingleTask
-
- ColumnLayout{
- anchors{
- top: parent.top
- left: parent.left
- right: parent.right
- }
- spacing: 5
-
- RowLayout{
- Layout.topMargin: 15
- Layout.leftMargin: 15
- spacing: 14
- FluText{
- text:"FluentUI"
- font: FluTextStyle.Title
- MouseArea{
- anchors.fill: parent
- onClicked: {
- FluApp.navigate("/")
- }
- }
- }
- FluText{
- text:"v%1".arg(AppInfo.version)
- font: FluTextStyle.Body
- Layout.alignment: Qt.AlignBottom
- }
- }
-
- RowLayout{
- spacing: 14
- Layout.leftMargin: 15
- FluText{
- text:"作者:"
- }
- FluText{
- text:"朱子楚"
- Layout.alignment: Qt.AlignBottom
- }
- }
-
- RowLayout{
- spacing: 14
- Layout.leftMargin: 15
- FluText{
- text:"微信号:"
- }
- FluText{
- text:"FluentUI"
- Layout.alignment: Qt.AlignBottom
- }
- FluText{
- text:"(有啥问题可能不会马上回,但发了红包必须立马回......)"
- }
- }
-
- RowLayout{
- spacing: 14
- Layout.leftMargin: 15
- FluText{
- text:"GitHub:"
- }
- FluTextButton{
- id:text_hublink
- topPadding:0
- bottomPadding:0
- text:"https://github.com/zhuzichu520/FluentUI"
- Layout.alignment: Qt.AlignBottom
- onClicked: {
- Qt.openUrlExternally(text_hublink.text)
- }
- }
- }
-
- RowLayout{
- spacing: 14
- Layout.leftMargin: 15
- FluText{
- text:"B站:"
- }
- FluTextButton{
- topPadding:0
- bottomPadding:0
- text:"https://www.bilibili.com/video/BV1mg4y1M71w/"
- Layout.alignment: Qt.AlignBottom
- onClicked: {
- Qt.openUrlExternally(text)
- }
- }
- }
-
- RowLayout{
- spacing: 14
- Layout.leftMargin: 15
- FluText{
- id:text_info
- text:"如果该项目对你有作用,就请点击上方链接给一个免费的star,或者一键三连,谢谢!"
- ColorAnimation {
- id: animation
- target: text_info
- property: "color"
- from: "red"
- to: "blue"
- duration: 1000
- running: true
- loops: Animation.Infinite
- easing.type: Easing.InOutQuad
- }
- }
- }
-
- RowLayout{
- spacing: 14
- Layout.topMargin: 20
- Layout.leftMargin: 15
- FluText{
- text:"捐赠:"
- }
- }
-
- Item{
- Layout.preferredWidth: parent.width
- Layout.preferredHeight: 252
- Row{
- anchors.horizontalCenter: parent.horizontalCenter
- spacing: 30
- Image{
- width: 250
- height: 250
- source: "qrc:/example/res/image/qrcode_wx.jpg"
- }
- Image{
- width: 250
- height: 250
- source: "qrc:/example/res/image/qrcode_zfb.jpg"
- }
- }
- }
-
- RowLayout{
- spacing: 14
- Layout.leftMargin: 15
- Layout.topMargin: 20
- FluText{
- id:text_desc
- text:"个人开发,维护不易,你们的捐赠就是我继续更新的动力!\n有什么问题提Issues,只要时间充足我就会解决的!!"
- }
- }
- }
-}
diff --git a/example/qml-Qt6/window/CrashWindow.qml b/example/qml-Qt6/window/CrashWindow.qml
deleted file mode 100644
index e5d4443d..00000000
--- a/example/qml-Qt6/window/CrashWindow.qml
+++ /dev/null
@@ -1,79 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import FluentUI
-import Qt.labs.platform
-import "../component"
-
-FluWindow {
-
- id:window
- title: qsTr("Friendly Reminder")
- width: 300
- height: 400
- fixSize: true
- showMinimize: false
-
- property string crashFilePath
-
- Component.onCompleted: {
- window.stayTop = true
- }
-
- onInitArgument:
- (argument)=>{
- crashFilePath = argument.crashFilePath
- }
-
- Image{
- width: 540/2
- height: 285/2
- anchors{
- horizontalCenter: parent.horizontalCenter
- top: parent.top
- topMargin: 40
- }
- source: "qrc:/example/res/image/ic_crash.png"
- }
-
- FluText{
- id:text_info
- anchors{
- top: parent.top
- topMargin: 240
- left: parent.left
- right: parent.right
- leftMargin: 10
- rightMargin: 10
- }
- wrapMode: Text.WrapAnywhere
- text: qsTr("We apologize for the inconvenience caused by an unexpected error")
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- }
-
- RowLayout{
- anchors{
- horizontalCenter: parent.horizontalCenter
- bottom: parent.bottom
- bottomMargin: 20
- }
- FluButton{
- text: qsTr("Report Logs")
- onClicked: {
- FluTools.showFileInFolder(crashFilePath)
- }
- }
- Item{
- width: 30
- height: 1
- }
- FluFilledButton{
- text: qsTr("Restart Program")
- onClicked: {
- FluApp.exit(931)
- }
- }
- }
-
-}
diff --git a/example/qml-Qt6/window/FluentInitalizrWindow.qml b/example/qml-Qt6/window/FluentInitalizrWindow.qml
deleted file mode 100644
index e679ba88..00000000
--- a/example/qml-Qt6/window/FluentInitalizrWindow.qml
+++ /dev/null
@@ -1,113 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import FluentUI
-import Qt.labs.platform
-import "../component"
-
-FluWindowDialog {
-
- id:window
- title:qsTr("FluentUI Initalizr")
- width: 600
- height: 400
-
- contentDelegate:Component{
- Item{
- Connections{
- target: InitalizrHelper
- function onError(message){
- showError(message)
- }
- function onSuccess(path){
- FluTools.showFileInFolder(path+"/CMakeLists.txt")
- window.close()
- }
- }
-
- FluText{
- id:text_title
- text:qsTr("FluentUI Initalizr")
- font: FluTextStyle.Title
- anchors{
- left: parent.left
- top: parent.top
- leftMargin: 20
- topMargin: 20
- }
- }
-
- Column{
- spacing: 14
- anchors{
- left: parent.left
- top: text_title.bottom
- leftMargin: 20
- topMargin: 20
- }
- FluTextBox{
- id:text_box_name
- width: 180
- placeholderText: qsTr("Name")
- focus: true
- }
- Row{
- spacing: 8
- FluTextBox{
- id:text_box_path
- width: 300
- placeholderText: qsTr("Create In")
- anchors.verticalCenter: parent.verticalCenter
- }
- FluButton{
- text:qsTr("Browse")
- anchors.verticalCenter: parent.verticalCenter
- onClicked: {
- folder_dialog.open()
- }
- }
- }
- }
-
- FolderDialog{
- id:folder_dialog
- onAccepted: {
- text_box_path.text = FluTools.toLocalPath(currentFolder)
- }
- }
-
- Rectangle{
- id:layout_actions
- width: parent.width
- height: 60
- anchors.bottom: parent.bottom
- color: FluTheme.backgroundColor
- Row{
- height: parent.height
- spacing: 20
- anchors{
- right: parent.right
- rightMargin: 20
- }
- FluButton{
- text:qsTr("Cancel")
- width: 120
- anchors.verticalCenter: parent.verticalCenter
- onClicked: {
- window.close()
- }
- }
- FluFilledButton{
- text:qsTr("Create")
- width: 120
- anchors.verticalCenter: parent.verticalCenter
- onClicked: {
- InitalizrHelper.generate(text_box_name.text,text_box_path.text)
- }
- }
- }
- }
- }
- }
-
-}
diff --git a/example/qml-Qt6/window/HotloadWindow.qml b/example/qml-Qt6/window/HotloadWindow.qml
deleted file mode 100644
index 7ae6be9d..00000000
--- a/example/qml-Qt6/window/HotloadWindow.qml
+++ /dev/null
@@ -1,97 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import FluentUI
-import example
-import "../component"
-
-FluWindow {
-
- id:window
- title: qsTr("Hot Loader")
- width: 800
- height: 600
- minimumWidth: 520
- minimumHeight: 200
- launchMode: FluWindowType.SingleTask
- FileWatcher{
- id:watcher
- onFileChanged: {
- loader.reload()
- }
- }
- FluArea{
- anchors.fill: parent
- FluRemoteLoader{
- id:loader
- anchors.fill: parent
- statusMode: FluStatusLayoutType.Success
- lazy: true
- errorItem: Item{
- FluText{
- text:loader.itemLodaer().sourceComponent.errorString()
- color:"red"
- anchors.fill: parent
- wrapMode: Text.WrapAnywhere
- padding: 20
- verticalAlignment: Qt.AlignVCenter
- horizontalAlignment: Qt.AlignHCenter
- }
- }
- }
- FluText{
- text: qsTr("Drag in a qml file")
- font.pixelSize: 26
- anchors.centerIn: parent
- visible: !loader.itemLodaer().item && loader.statusMode === FluStatusLayoutType.Success
- }
- Rectangle{
- radius: 4
- anchors.fill: parent
- color: "#33333333"
- visible: drop_area.containsDrag
- }
- DropArea{
- id:drop_area
- anchors.fill: parent
- onEntered:
- (event)=>{
- if(!event.hasUrls){
- event.accepted = false
- return
- }
- var url = getUrlByEvent(event)
- if(url === ""){
- event.accepted = false
- return
- }
- var fileExtension = url.substring(url.lastIndexOf(".") + 1)
- if (fileExtension !== "qml") {
- event.accepted = false
- return
- }
- return true
- }
- onDropped:
- (event)=>{
- var url = getUrlByEvent(event)
- if(url !== ""){
- loader.source = url
- watcher.path = url
- loader.reload()
- }
- }
- }
- }
-
- function getUrlByEvent(event){
- var url = ""
- if (event.urls.length === 0) {
- url = "file:///"+event.getDataAsString("text/plain")
- }else{
- url = event.urls[0].toString()
- }
- return url
- }
-
-}
diff --git a/example/qml-Qt6/window/LoginWindow.qml b/example/qml-Qt6/window/LoginWindow.qml
deleted file mode 100644
index 23f95972..00000000
--- a/example/qml-Qt6/window/LoginWindow.qml
+++ /dev/null
@@ -1,59 +0,0 @@
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Controls
-import FluentUI
-import "../component"
-
-FluWindow {
-
- id: window
- title: qsTr("Login")
- width: 400
- height: 400
- fixSize: true
- modality: Qt.ApplicationModal
- onInitArgument:
- (argument)=>{
- textbox_uesrname.updateText(argument.username)
- textbox_password.focus = true
- }
-
- ColumnLayout{
- anchors{
- left: parent.left
- right: parent.right
- verticalCenter: parent.verticalCenter
- }
-
- FluAutoSuggestBox{
- id: textbox_uesrname
- items:[{title:"Admin"},{title:"User"}]
- placeholderText: qsTr("Please enter the account")
- Layout.preferredWidth: 260
- Layout.alignment: Qt.AlignHCenter
- }
-
- FluTextBox{
- id: textbox_password
- Layout.topMargin: 20
- Layout.preferredWidth: 260
- placeholderText: qsTr("Please enter your password")
- echoMode:TextInput.Password
- Layout.alignment: Qt.AlignHCenter
- }
-
- FluFilledButton{
- text: qsTr("Login")
- Layout.alignment: Qt.AlignHCenter
- Layout.topMargin: 20
- onClicked:{
- if(textbox_password.text === ""){
- showError(qsTr("Please feel free to enter a password"))
- return
- }
- onResult({password:textbox_password.text})
- window.close()
- }
- }
- }
-}
diff --git a/example/qml-Qt6/window/MainWindow.qml b/example/qml-Qt6/window/MainWindow.qml
deleted file mode 100644
index 5675b006..00000000
--- a/example/qml-Qt6/window/MainWindow.qml
+++ /dev/null
@@ -1,395 +0,0 @@
-import QtQuick
-import QtQuick.Window
-import QtQuick.Controls
-import QtQuick.Layouts
-import Qt.labs.platform
-import FluentUI
-import example
-import "../component"
-import "../global"
-import "../viewmodel"
-
-FluWindow {
-
- id:window
- title: "FluentUI"
- width: 1000
- height: 680
- minimumWidth: 520
- minimumHeight: 200
- launchMode: FluWindowType.SingleTask
- fitsAppBarWindows: true
- appBar: FluAppBar {
- height: 30
- showDark: true
- darkClickListener:(button)=>handleDarkChanged(button)
- closeClickListener: ()=>{dialog_close.open()}
- z:7
- }
-
- SettingsViewModel{
- id:viewmodel_settings
- }
-
- FluEvent{
- id:event_checkupdate
- name: "checkUpdate"
- onTriggered: {
- checkUpdate(false)
- }
- }
-
- onFirstVisible: {
- timer_tour_delay.restart()
- }
-
- Timer{
- id:timer_tour_delay
- interval: 200
- onTriggered: {
- tour.open()
- }
- }
-
- Component.onCompleted: {
- checkUpdate(true)
- FluEventBus.registerEvent(event_checkupdate)
- }
-
- Component.onDestruction: {
- FluEventBus.unRegisterEvent(event_checkupdate)
- }
-
- SystemTrayIcon {
- id:system_tray
- visible: true
- icon.source: "qrc:/example/res/image/favicon.ico"
- tooltip: "FluentUI"
- menu: Menu {
- MenuItem {
- text: "退出"
- onTriggered: {
- FluApp.exit()
- }
- }
- }
- onActivated:
- (reason)=>{
- if(reason === SystemTrayIcon.Trigger){
- window.show()
- window.raise()
- window.requestActivate()
- }
- }
- }
-
- Timer{
- id: timer_window_hide_delay
- interval: 150
- onTriggered: {
- window.hide()
- }
- }
-
- FluContentDialog{
- id: dialog_close
- title: qsTr("Quit")
- message: qsTr("Are you sure you want to exit the program?")
- negativeText: qsTr("Minimize")
- buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.NeutralButton | FluContentDialogType.PositiveButton
- onNegativeClicked: {
- system_tray.showMessage(qsTr("Friendly Reminder"),qsTr("FluentUI is hidden from the tray, click on the tray to activate the window again"));
- timer_window_hide_delay.restart()
- }
- positiveText: qsTr("Quit")
- neutralText: qsTr("Cancel")
- onPositiveClicked:{
- FluApp.exit(0)
- }
- }
-
- Component{
- id: nav_item_right_menu
- FluMenu{
- width: 186
- FluMenuItem{
- text: qsTr("Open in Separate Window")
- font.pixelSize: 12
- onClicked: {
- FluApp.navigate("/pageWindow",{title:modelData.title,url:modelData.url})
- }
- }
- }
- }
-
- Flipable{
- id:flipable
- anchors.fill: parent
- property bool flipped: false
- property real flipAngle: 0
- transform: Rotation {
- id: rotation
- origin.x: flipable.width/2
- origin.y: flipable.height/2
- axis { x: 0; y: 1; z: 0 }
- angle: flipable.flipAngle
-
- }
- states: State {
- PropertyChanges { target: flipable; flipAngle: 180 }
- when: flipable.flipped
- }
- transitions: Transition {
- NumberAnimation { target: flipable; property: "flipAngle"; duration: 1000 ; easing.type: Easing.OutCubic}
- }
- back: Item{
- anchors.fill: flipable
- visible: flipable.flipAngle !== 0
- Row{
- id:layout_back_buttons
- z:8
- anchors{
- top: parent.top
- left: parent.left
- topMargin: FluTools.isMacos() ? 20 : 5
- leftMargin: 5
- }
- FluIconButton{
- iconSource: FluentIcons.ChromeBack
- width: 30
- height: 30
- iconSize: 13
- onClicked: {
- flipable.flipped = false
- }
- }
- FluIconButton{
- iconSource: FluentIcons.Sync
- width: 30
- height: 30
- iconSize: 13
- onClicked: {
- loader.reload()
- }
- }
- Component.onCompleted: {
- appBar.setHitTestVisible(layout_back_buttons)
- }
- }
- FluRemoteLoader{
- id:loader
- lazy: true
- anchors.fill: parent
- source: "https://zhu-zichu.gitee.io/Qt_168_LieflatPage.qml"
- }
- }
- front: Item{
- id:page_front
- visible: flipable.flipAngle !== 180
- anchors.fill: flipable
- FluNavigationView{
- property int clickCount: 0
- id:nav_view
- width: parent.width
- height: parent.height
- z:999
- //Stack模式,每次切换都会将页面压入栈中,随着栈的页面增多,消耗的内存也越多,内存消耗多就会卡顿,这时候就需要按返回将页面pop掉,释放内存。该模式可以配合FluPage中的launchMode属性,设置页面的启动模式
- // pageMode: FluNavigationViewType.Stack
- //NoStack模式,每次切换都会销毁之前的页面然后创建一个新的页面,只需消耗少量内存,可以配合FluViewModel保存页面数据(推荐)
- pageMode: FluNavigationViewType.NoStack
- items: ItemsOriginal
- footerItems:ItemsFooter
- topPadding:{
- if(window.useSystemAppBar){
- return 0
- }
- return FluTools.isMacos() ? 20 : 0
- }
- displayMode:viewmodel_settings.displayMode
- logo: "qrc:/example/res/image/favicon.ico"
- title:"FluentUI"
- onLogoClicked:{
- clickCount += 1
- showSuccess("%1:%2".arg(qsTr("Click Time")).arg(clickCount))
- if(clickCount === 5){
- loader.reload()
- flipable.flipped = true
- clickCount = 0
- }
- }
- autoSuggestBox:FluAutoSuggestBox{
- iconSource: FluentIcons.Search
- items: ItemsOriginal.getSearchData()
- placeholderText: qsTr("Search")
- onItemClicked:
- (data)=>{
- ItemsOriginal.startPageByItem(data)
- }
- }
- Component.onCompleted: {
- ItemsOriginal.navigationView = nav_view
- ItemsOriginal.paneItemMenu = nav_item_right_menu
- ItemsFooter.navigationView = nav_view
- ItemsFooter.paneItemMenu = nav_item_right_menu
- appBar.setHitTestVisible(nav_view.buttonMenu)
- appBar.setHitTestVisible(nav_view.buttonBack)
- appBar.setHitTestVisible(nav_view.imageLogo)
- setCurrentIndex(0)
- }
- }
- }
- }
-
- Component{
- id:com_reveal
- CircularReveal{
- id:reveal
- target:window.contentItem
- anchors.fill: parent
- onAnimationFinished:{
- //动画结束后释放资源
- loader_reveal.sourceComponent = undefined
- }
- onImageChanged: {
- changeDark()
- }
- }
- }
-
- FluLoader{
- id:loader_reveal
- anchors.fill: parent
- }
-
- function distance(x1,y1,x2,y2){
- return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))
- }
-
- function handleDarkChanged(button){
- if(!FluTheme.enableAnimation || window.fitsAppBarWindows === false){
- changeDark()
- }else{
- if(loader_reveal.sourceComponent){
- return
- }
- loader_reveal.sourceComponent = com_reveal
- var target = window.contentItem
- var pos = button.mapToItem(target,0,0)
- var mouseX = pos.x
- var mouseY = pos.y
- var radius = Math.max(distance(mouseX,mouseY,0,0),distance(mouseX,mouseY,target.width,0),distance(mouseX,mouseY,0,target.height),distance(mouseX,mouseY,target.width,target.height))
- var reveal = loader_reveal.item
- reveal.start(reveal.width*Screen.devicePixelRatio,reveal.height*Screen.devicePixelRatio,Qt.point(mouseX,mouseY),radius)
- }
- }
-
- function changeDark(){
- if(FluTheme.dark){
- FluTheme.darkMode = FluThemeType.Light
- }else{
- FluTheme.darkMode = FluThemeType.Dark
- }
- }
-
- Shortcut {
- sequence: "F5"
- context: Qt.WindowShortcut
- onActivated: {
- if(flipable.flipped){
- loader.reload()
- }
- }
- }
-
- Shortcut {
- sequence: "F6"
- context: Qt.WindowShortcut
- onActivated: {
- tour.open()
- }
- }
-
- FluTour{
- id: tour
- finishText: qsTr("Finish")
- nextText: qsTr("Next")
- previousText: qsTr("Previous")
- steps:{
- var data = []
- if(!window.useSystemAppBar){
- data.push({title:qsTr("Dark Mode"),description: qsTr("Here you can switch to night mode."),target:()=>appBar.buttonDark})
- }
- data.push({title:qsTr("Hide Easter eggs"),description: qsTr("Try a few more clicks!!"),target:()=>nav_view.imageLogo})
- return data
- }
- }
-
- FpsItem{
- id:fps_item
- }
-
- FluText{
- text: "fps %1".arg(fps_item.fps)
- opacity: 0.3
- anchors{
- bottom: parent.bottom
- right: parent.right
- bottomMargin: 5
- rightMargin: 5
- }
- }
-
- FluContentDialog{
- property string newVerson
- property string body
- id: dialog_update
- title: qsTr("Upgrade Tips")
- message:qsTr("FluentUI is currently up to date ")+ newVerson +qsTr(" -- The current app version") +AppInfo.version+qsTr(" \nNow go and download the new version?\n\nUpdated content: \n")+body
- buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
- negativeText: qsTr("Cancel")
- positiveText: qsTr("OK")
- onPositiveClicked:{
- Qt.openUrlExternally("https://github.com/zhuzichu520/FluentUI/releases/latest")
- }
- }
-
- FluNetworkCallable{
- id:callable
- property bool silent: true
- onStart: {
- console.debug("satrt check update...")
- }
- onFinish: {
- console.debug("check update finish")
- FluEventBus.post("checkUpdateFinish");
- }
- onSuccess:
- (result)=>{
- var data = JSON.parse(result)
- console.debug("current version "+AppInfo.version)
- console.debug("new version "+data.tag_name)
- if(data.tag_name !== AppInfo.version){
- dialog_update.newVerson = data.tag_name
- dialog_update.body = data.body
- dialog_update.open()
- }else{
- if(!silent){
- showInfo(qsTr("The current version is already the latest"))
- }
- }
- }
- onError:
- (status,errorString)=>{
- if(!silent){
- showError(qsTr("The network is abnormal"))
- }
- console.debug(status+";"+errorString)
- }
- }
-
- function checkUpdate(silent){
- callable.silent = silent
- FluNetwork.get("https://api.github.com/repos/zhuzichu520/FluentUI/releases/latest")
- .go(callable)
- }
-}
diff --git a/example/qml-Qt6/window/PageWindow.qml b/example/qml-Qt6/window/PageWindow.qml
deleted file mode 100644
index b4057e1e..00000000
--- a/example/qml-Qt6/window/PageWindow.qml
+++ /dev/null
@@ -1,25 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import FluentUI
-import example
-import "../component"
-
-FluWindow {
-
- id:window
- width: 800
- height: 600
- minimumWidth: 520
- minimumHeight: 200
- launchMode: FluWindowType.SingleInstance
- onInitArgument:
- (arg)=>{
- window.title = arg.title
- loader.setSource( arg.url,{animDisabled:true})
- }
- FluLoader{
- id: loader
- anchors.fill: parent
- }
-}
diff --git a/example/qml-Qt6/window/SingleInstanceWindow.qml b/example/qml-Qt6/window/SingleInstanceWindow.qml
deleted file mode 100644
index aca0d519..00000000
--- a/example/qml-Qt6/window/SingleInstanceWindow.qml
+++ /dev/null
@@ -1,35 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import FluentUI
-import "../component"
-
-FluWindow {
-
- id: window
- title: qsTr("SingleInstance")
- width: 500
- height: 600
- fixSize: true
- launchMode: FluWindowType.SingleInstance
-
- FluTextBox{
- anchors{
- top:parent.top
- topMargin:60
- horizontalCenter: parent.horizontalCenter
- }
- }
-
- FluText{
- wrapMode: Text.WrapAnywhere
- anchors{
- left: parent.left
- right: parent.right
- leftMargin: 20
- rightMargin: 20
- verticalCenter: parent.verticalCenter
- }
- text: qsTr("I'm a SingleInstance window, and if I exist, I'll destroy the previous window and create a new one")
- }
-}
diff --git a/example/qml-Qt6/window/SingleTaskWindow.qml b/example/qml-Qt6/window/SingleTaskWindow.qml
deleted file mode 100644
index a43cc042..00000000
--- a/example/qml-Qt6/window/SingleTaskWindow.qml
+++ /dev/null
@@ -1,21 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import FluentUI
-import "../component"
-
-FluWindow {
-
- id: window
- title: qsTr("SingleTask")
- width: 500
- height: 600
- fixSize: true
- launchMode: FluWindowType.SingleTask
-
- FluText{
- anchors.centerIn: parent
- text: qsTr("I'm a SingleTask mode window, and if I exist, I activate the window")
- }
-
-}
diff --git a/example/qml-Qt6/window/StandardWindow.qml b/example/qml-Qt6/window/StandardWindow.qml
deleted file mode 100644
index 13e60a8a..00000000
--- a/example/qml-Qt6/window/StandardWindow.qml
+++ /dev/null
@@ -1,43 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import FluentUI
-import "../component"
-
-FluWindow {
-
- id:window
- title:"Standard"
- width: 500
- height: 600
- fixSize: true
- launchMode: FluWindowType.Standard
-
- FluMenuBar {
- FluMenu {
- title: qsTr("File")
- Action { text: qsTr("New...") }
- Action { text: qsTr("Open...") }
- Action { text: qsTr("Save") }
- Action { text: qsTr("Save As...") }
- FluMenuSeparator { }
- Action { text: qsTr("Quit") }
- }
- FluMenu {
- title: qsTr("Edit")
- Action { text: qsTr("Cut") }
- Action { text: qsTr("Copy") }
- Action { text: qsTr("Paste") }
- }
- FluMenu {
- title: qsTr("Help")
- Action { text: qsTr("About") }
- }
- }
-
- FluText{
- anchors.centerIn: parent
- text: qsTr("I'm a Standard mode window, and every time I create a new window")
- }
-
-}
diff --git a/example/qml/window/MainWindow.qml b/example/qml/window/MainWindow.qml
index 4b49563a..ed85f702 100644
--- a/example/qml/window/MainWindow.qml
+++ b/example/qml/window/MainWindow.qml
@@ -2,6 +2,7 @@ import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
+import QtQml 2.15
import Qt.labs.platform 1.1
import FluentUI 1.0
import example 1.0
diff --git a/example/res/template/src/CMakeLists.txt.in b/example/res/template/src/CMakeLists.txt.in
index 6d1d3717..34946eaa 100644
--- a/example/res/template/src/CMakeLists.txt.in
+++ b/example/res/template/src/CMakeLists.txt.in
@@ -84,9 +84,9 @@ if (CMAKE_BUILD_TYPE MATCHES "Release")
add_custom_target(Script-DeployRelease
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_SOURCE_DIR}/dist
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${CMAKE_SOURCE_DIR}/dist
- COMMAND ${QT_DEPLOY_QT} ${CMAKE_SOURCE_DIR}/dist/%1 -qmldir=${CMAKE_CURRENT_LIST_DIR}
- COMMENT "Deploying Qt Dependencies After Build........."
- SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/README.md
+ COMMAND ${QT_DEPLOY_QT} ${CMAKE_SOURCE_DIR}/dist/${PROJECT_NAME} -qmldir=${CMAKE_CURRENT_LIST_DIR}
+ COMMENT "MacOs Deploying Qt Dependencies After Build........."
+ SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
endif()
@@ -95,10 +95,11 @@ if (CMAKE_BUILD_TYPE MATCHES "Release")
add_custom_target(Script-DeployRelease
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_SOURCE_DIR}/dist
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${CMAKE_SOURCE_DIR}/dist
- COMMAND ${QT_DEPLOY_QT} ${CMAKE_SOURCE_DIR}/dist/%1.exe -qmldir=${CMAKE_CURRENT_LIST_DIR}
- COMMENT "Deploying Qt Dependencies After Build........."
- SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/README.md
+ COMMAND ${QT_DEPLOY_QT} ${CMAKE_SOURCE_DIR}/dist/${PROJECT_NAME}.exe -qmldir=${CMAKE_CURRENT_LIST_DIR}
+ COMMENT "Windows Deploying Qt Dependencies After Build........."
+ SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
endif()
endif()
+
diff --git a/example/res/template/src/README.md.in b/example/res/template/src/README.md.in
new file mode 100644
index 00000000..2ac81422
--- /dev/null
+++ b/example/res/template/src/README.md.in
@@ -0,0 +1,18 @@
+# FluentUI 脚手架开发说明
+
+## 代码说明
+
+文件编码格式请用utf-8不带bom,代码中最好不要含有中文(包括注释),中文可能会改变文件格式导致编译失败,还会出现乱码,显示中文请用国际化,c++中用tr函数,qml中用qsTr函数
+
+### 脚本说明
+
+1. **Script-UpdateTranslations**
+
+ 用于更新ts与qm文件,当你的代码添加了tr或者qsTr函数后,执行这个脚本会更新ts文件,然后编写翻译后,再执行这个脚本,qm文件会更新生效
+
+2. **Script-DeployRelease**
+
+ 执行Qt的windeployqt或macdeployqt命令,这个脚本只在windows与macos才有,linux不支持
+
+
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f153f419..7a50f9b5 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -13,7 +13,7 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
#设置版本号
-add_definitions(-DFLUENTUI_VERSION=1,7,2,0)
+add_definitions(-DFLUENTUI_VERSION=1,7,3,0)
if (FLUENTUI_BUILD_STATIC_LIB)
add_definitions(-DFLUENTUI_BUILD_STATIC_LIB)
@@ -170,9 +170,9 @@ endif()
#链接库
target_link_libraries(${PROJECT_NAME} PUBLIC
- Qt${QT_VERSION_MAJOR}::CorePrivate
- Qt${QT_VERSION_MAJOR}::QuickPrivate
- Qt${QT_VERSION_MAJOR}::QmlPrivate
+ Qt${QT_VERSION_MAJOR}::Core
+ Qt${QT_VERSION_MAJOR}::Quick
+ Qt${QT_VERSION_MAJOR}::Qml
)
#安装
diff --git a/src/FluTheme.cpp b/src/FluTheme.cpp
index 065c43c1..d41b568e 100644
--- a/src/FluTheme.cpp
+++ b/src/FluTheme.cpp
@@ -1,14 +1,7 @@
#include "FluTheme.h"
#include
-#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0))
-#include
-#elif ((QT_VERSION >= QT_VERSION_CHECK(6, 2, 1)))
-#include
-#include
-#else
#include
-#endif
#include "Def.h"
#include "FluColors.h"
@@ -71,18 +64,9 @@ QJsonArray FluTheme::awesomeList(const QString& keyword){
}
bool FluTheme::systemDark(){
-#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0))
- return (QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark);
-#elif ((QT_VERSION >= QT_VERSION_CHECK(6, 2, 1)))
- if (const QPlatformTheme * const theme = QGuiApplicationPrivate::platformTheme()) {
- return (theme->appearance() == QPlatformTheme::Appearance::Dark);
- }
- return false;
-#else
QPalette palette = qApp->palette();
QColor color = palette.color(QPalette::Window).rgb();
return !(color.red() * 0.2126 + color.green() * 0.7152 + color.blue() * 0.0722 > 255 / 2);
-#endif
}
bool FluTheme::dark(){
diff --git a/src/FluentUI/Controls/FluAcrylic.qml b/src/FluentUI/Controls/FluAcrylic.qml
new file mode 100644
index 00000000..035fe423
--- /dev/null
+++ b/src/FluentUI/Controls/FluAcrylic.qml
@@ -0,0 +1,41 @@
+import QtQuick
+import Qt5Compat.GraphicalEffects
+import FluentUI
+
+Item {
+ id: control
+ property color tintColor: Qt.rgba(1, 1, 1, 1)
+ property real tintOpacity: 0.65
+ property real luminosity: 0.01
+ property real noiseOpacity: 0.02
+ property alias target: effect_source.sourceItem
+ property int blurRadius: 32
+ property rect targetRect: Qt.rect(control.x, control.y, control.width,
+ control.height)
+ ShaderEffectSource {
+ id: effect_source
+ anchors.fill: parent
+ visible: false
+ sourceRect: control.targetRect
+ }
+ FastBlur {
+ id: fast_blur
+ anchors.fill: parent
+ source: effect_source
+ radius: control.blurRadius
+ }
+ Rectangle {
+ anchors.fill: parent
+ color: Qt.rgba(1, 1, 1, luminosity)
+ }
+ Rectangle {
+ anchors.fill: parent
+ color: Qt.rgba(tintColor.r, tintColor.g, tintColor.b, tintOpacity)
+ }
+ Image {
+ anchors.fill: parent
+ source: "../Image/noise.png"
+ fillMode: Image.Tile
+ opacity: control.noiseOpacity
+ }
+}
diff --git a/src/FluentUI/Controls/FluAppBar.qml b/src/FluentUI/Controls/FluAppBar.qml
new file mode 100644
index 00000000..8a62854b
--- /dev/null
+++ b/src/FluentUI/Controls/FluAppBar.qml
@@ -0,0 +1,347 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Window
+import QtQuick.Layouts
+import FluentUI
+
+Rectangle{
+ property string title: ""
+ property string darkText : qsTr("Dark")
+ property string lightText : qsTr("Light")
+ property string minimizeText : qsTr("Minimize")
+ property string restoreText : qsTr("Restore")
+ property string maximizeText : qsTr("Maximize")
+ property string closeText : qsTr("Close")
+ property string stayTopText : qsTr("Sticky on Top")
+ property string stayTopCancelText : qsTr("Sticky on Top cancelled")
+ property color textColor: FluTheme.dark ? "#FFFFFF" : "#000000"
+ property color minimizeNormalColor: FluTheme.itemNormalColor
+ property color minimizeHoverColor: FluTheme.itemHoverColor
+ property color minimizePressColor: FluTheme.itemPressColor
+ property color maximizeNormalColor: FluTheme.itemNormalColor
+ property color maximizeHoverColor: FluTheme.itemHoverColor
+ property color maximizePressColor: FluTheme.itemPressColor
+ property color closeNormalColor: Qt.rgba(0,0,0,0)
+ property color closeHoverColor: Qt.rgba(251/255,115/255,115/255,1)
+ property color closePressColor: Qt.rgba(251/255,115/255,115/255,0.8)
+ property bool showDark: false
+ property bool showClose: true
+ property bool showMinimize: true
+ property bool showMaximize: true
+ property bool showStayTop: true
+ property bool titleVisible: true
+ property url icon
+ property int iconSize: 20
+ property bool isMac: FluTools.isMacos()
+ property color borerlessColor : FluTheme.primaryColor
+ property bool systemMoveEnable: true
+ property var maxClickListener : function(){
+ if(FluTools.isMacos()){
+ if (d.win.visibility === Window.FullScreen)
+ d.win.showNormal()
+ else
+ d.win.showFullScreen()
+ }else{
+ if (d.win.visibility === Window.Maximized)
+ d.win.showNormal()
+ else
+ d.win.showMaximized()
+ d.hoverMaxBtn = false
+ }
+ }
+ property var minClickListener: function(){
+ if(d.win.transientParent != null){
+ d.win.transientParent.showMinimized()
+ }else{
+ d.win.showMinimized()
+ }
+ }
+ property var closeClickListener : function(){
+ d.win.close()
+ }
+ property var stayTopClickListener: function(){
+ if(d.win instanceof FluWindow){
+ d.win.stayTop = !d.win.stayTop
+ }
+ }
+ property var darkClickListener: function(){
+ if(FluTheme.dark){
+ FluTheme.darkMode = FluThemeType.Light
+ }else{
+ FluTheme.darkMode = FluThemeType.Dark
+ }
+ }
+ property var systemMenuListener: function(){
+ if(d.win instanceof FluWindow){
+ d.win.showSystemMenu()
+ }
+ }
+ property alias buttonStayTop: btn_stay_top
+ property alias buttonMinimize: btn_minimize
+ property alias buttonMaximize: btn_maximize
+ property alias buttonClose: btn_close
+ property alias buttonDark: btn_dark
+ id:control
+ color: Qt.rgba(0,0,0,0)
+ height: visible ? 30 : 0
+ opacity: visible
+ z: 65535
+ Item{
+ id:d
+ property var hitTestList: []
+ property bool hoverMaxBtn: false
+ property var win: Window.window
+ property bool stayTop: {
+ if(d.win instanceof FluWindow){
+ return d.win.stayTop
+ }
+ return false
+ }
+ property bool isRestore: win && Window.Maximized === win.visibility
+ property bool resizable: win && !(win.height === win.maximumHeight && win.height === win.minimumHeight && win.width === win.maximumWidth && win.width === win.minimumWidth)
+ function containsPointToItem(point,item){
+ var pos = item.mapToGlobal(0,0)
+ var rect = Qt.rect(pos.x,pos.y,item.width,item.height)
+ if(point.x>rect.x && point.x<(rect.x+rect.width) && point.y>rect.y && point.y<(rect.y+rect.height)){
+ return true
+ }
+ return false
+ }
+ }
+ MouseArea{
+ id:mouse_app_bar
+ anchors.fill: parent
+ onPositionChanged:
+ (mouse)=>{
+ if(systemMoveEnable){
+ d.win.startSystemMove()
+ }
+ }
+ onDoubleClicked:
+ (mouse)=>{
+ if(systemMoveEnable && d.resizable && Qt.LeftButton){
+ btn_maximize.clicked()
+ }
+ }
+ acceptedButtons: Qt.LeftButton|Qt.RightButton
+ onClicked:
+ (mouse)=>{
+ if (systemMoveEnable && mouse.button === Qt.RightButton){
+ control.systemMenuListener()
+ }
+ }
+ }
+ Row{
+ anchors{
+ verticalCenter: parent.verticalCenter
+ left: isMac ? undefined : parent.left
+ leftMargin: isMac ? undefined : 10
+ horizontalCenter: isMac ? parent.horizontalCenter : undefined
+ }
+ spacing: 10
+ Image{
+ width: control.iconSize
+ height: control.iconSize
+ visible: status === Image.Ready ? true : false
+ source: control.icon
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ FluText {
+ text: title
+ visible: control.titleVisible
+ color:control.textColor
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ }
+
+ Component{
+ id:com_mac_buttons
+ RowLayout{
+ FluImageButton{
+ Layout.preferredHeight: 12
+ Layout.preferredWidth: 12
+ normalImage: "../Image/btn_close_normal.png"
+ hoveredImage: "../Image/btn_close_hovered.png"
+ pushedImage: "../Image/btn_close_pushed.png"
+ visible: showClose
+ onClicked: closeClickListener()
+ }
+ FluImageButton{
+ Layout.preferredHeight: 12
+ Layout.preferredWidth: 12
+ normalImage: "../Image/btn_min_normal.png"
+ hoveredImage: "../Image/btn_min_hovered.png"
+ pushedImage: "../Image/btn_min_pushed.png"
+ onClicked: minClickListener()
+ visible: showMinimize
+ }
+ FluImageButton{
+ Layout.preferredHeight: 12
+ Layout.preferredWidth: 12
+ normalImage: "../Image/btn_max_normal.png"
+ hoveredImage: "../Image/btn_max_hovered.png"
+ pushedImage: "../Image/btn_max_pushed.png"
+ onClicked: maxClickListener()
+ visible: d.resizable && showMaximize
+ }
+ }
+ }
+
+ FluLoader{
+ anchors{
+ verticalCenter: parent.verticalCenter
+ left: parent.left
+ leftMargin: 10
+ }
+ sourceComponent: isMac ? com_mac_buttons : undefined
+ }
+
+ RowLayout{
+ id:layout_row
+ anchors.right: parent.right
+ height: control.height
+ spacing: 0
+ Component.onCompleted: {
+ setHitTestVisible(layout_row)
+ }
+ FluIconButton{
+ id:btn_dark
+ Layout.preferredWidth: 40
+ Layout.preferredHeight: 30
+ padding: 0
+ verticalPadding: 0
+ horizontalPadding: 0
+ rightPadding: 2
+ iconSource: FluTheme.dark ? FluentIcons.Brightness : FluentIcons.QuietHours
+ Layout.alignment: Qt.AlignVCenter
+ iconSize: 15
+ visible: showDark
+ text: FluTheme.dark ? control.lightText : control.darkText
+ radius: 0
+ iconColor:control.textColor
+ onClicked:()=> darkClickListener(btn_dark)
+ }
+ FluIconButton{
+ id:btn_stay_top
+ Layout.preferredWidth: 40
+ Layout.preferredHeight: 30
+ padding: 0
+ verticalPadding: 0
+ horizontalPadding: 0
+ iconSource : FluentIcons.Pinned
+ Layout.alignment: Qt.AlignVCenter
+ iconSize: 14
+ visible: {
+ if(!(d.win instanceof FluWindow)){
+ return false
+ }
+ return showStayTop
+ }
+ text:d.stayTop ? control.stayTopCancelText : control.stayTopText
+ radius: 0
+ iconColor: d.stayTop ? FluTheme.primaryColor : control.textColor
+ onClicked: stayTopClickListener()
+ }
+ FluIconButton{
+ id:btn_minimize
+ Layout.preferredWidth: 40
+ Layout.preferredHeight: 30
+ padding: 0
+ verticalPadding: 0
+ horizontalPadding: 0
+ iconSource : FluentIcons.ChromeMinimize
+ Layout.alignment: Qt.AlignVCenter
+ iconSize: 11
+ text:minimizeText
+ radius: 0
+ visible: !isMac && showMinimize
+ iconColor: control.textColor
+ color: {
+ if(pressed){
+ return minimizePressColor
+ }
+ return hovered ? minimizeHoverColor : minimizeNormalColor
+ }
+ onClicked: minClickListener()
+ }
+ FluIconButton{
+ id:btn_maximize
+ Layout.preferredWidth: 40
+ Layout.preferredHeight: 30
+ padding: 0
+ verticalPadding: 0
+ horizontalPadding: 0
+ iconSource : d.isRestore ? FluentIcons.ChromeRestore : FluentIcons.ChromeMaximize
+ color: {
+ if(down){
+ return maximizePressColor
+ }
+ if(FluTools.isWindows11OrGreater()){
+ return d.hoverMaxBtn ? maximizeHoverColor : maximizeNormalColor
+ }
+ return hovered ? maximizeHoverColor : maximizeNormalColor
+ }
+ Layout.alignment: Qt.AlignVCenter
+ visible: d.resizable && !isMac && showMaximize
+ radius: 0
+ iconColor: control.textColor
+ text:d.isRestore?restoreText:maximizeText
+ iconSize: 11
+ onClicked: maxClickListener()
+ }
+ FluIconButton{
+ id:btn_close
+ Layout.preferredWidth: 40
+ Layout.preferredHeight: 30
+ padding: 0
+ verticalPadding: 0
+ horizontalPadding: 0
+ iconSource : FluentIcons.ChromeClose
+ Layout.alignment: Qt.AlignVCenter
+ text:closeText
+ visible: !isMac && showClose
+ radius: 0
+ iconSize: 10
+ iconColor: hovered ? Qt.rgba(1,1,1,1) : control.textColor
+ color:{
+ if(pressed){
+ return closePressColor
+ }
+ return hovered ? closeHoverColor : closeNormalColor
+ }
+ onClicked: closeClickListener()
+ }
+ }
+ function _maximizeButtonHover(){
+ var hover = false
+ if(btn_maximize.visible && FluTools.isWindows11OrGreater() && d.resizable){
+ if(d.containsPointToItem(FluTools.cursorPos(),btn_maximize)){
+ hover = true
+ }else{
+ if(btn_maximize.down){
+ btn_maximize.down = false
+ }
+ }
+ }
+ d.hoverMaxBtn = hover
+ return hover;
+ }
+ function _appBarHover(){
+ var cursorPos = FluTools.cursorPos()
+ for(var i =0 ;i< d.hitTestList.length; i++){
+ var item = d.hitTestList[i]
+ if(item.visible){
+ if(d.containsPointToItem(cursorPos,item)){
+ return false
+ }
+ }
+ }
+ if(d.containsPointToItem(cursorPos,control)){
+ return true
+ }
+ return false
+ }
+ function setHitTestVisible(id){
+ d.hitTestList.push(id)
+ }
+}
diff --git a/src/FluentUI/Controls/FluArea.qml b/src/FluentUI/Controls/FluArea.qml
new file mode 100644
index 00000000..81a08036
--- /dev/null
+++ b/src/FluentUI/Controls/FluArea.qml
@@ -0,0 +1,28 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Window
+import FluentUI
+
+Rectangle {
+ default property alias contentData : layout_content.data
+ property int paddings : 0
+ property int leftPadding : 0
+ property int rightPadding : 0
+ property int topPadding : 0
+ property int bottomPadding : 0
+ id:control
+ radius: 4
+ color: FluTheme.dark ? Window.active ? Qt.rgba(38/255,44/255,54/255,1) : Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1)
+ border.color: FluTheme.dark ? Window.active ? Qt.rgba(55/255,55/255,55/255,1):Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,229/255,234/255,1)
+ border.width: 1
+ implicitHeight: height
+ implicitWidth: width
+ Item {
+ id: layout_content
+ anchors.fill: parent
+ anchors.leftMargin: Math.max(paddings,leftPadding)
+ anchors.rightMargin: Math.max(paddings,rightPadding)
+ anchors.topMargin: Math.max(paddings,topPadding)
+ anchors.bottomMargin: Math.max(paddings,bottomPadding)
+ }
+}
diff --git a/src/FluentUI/Controls/FluAutoSuggestBox.qml b/src/FluentUI/Controls/FluAutoSuggestBox.qml
new file mode 100644
index 00000000..0aaa8e03
--- /dev/null
+++ b/src/FluentUI/Controls/FluAutoSuggestBox.qml
@@ -0,0 +1,139 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+FluTextBox{
+ property var items:[]
+ property string emptyText: qsTr("No results found")
+ property int autoSuggestBoxReplacement: FluentIcons.Search
+ property var filter: function(item){
+ if(item.title.indexOf(control.text)!==-1){
+ return true
+ }
+ return false
+ }
+ signal itemClicked(var data)
+ id:control
+ Component.onCompleted: {
+ d.loadData()
+ }
+ Item{
+ id:d
+ property bool flagVisible: true
+ property var window : Window.window
+ function handleClick(modelData){
+ control_popup.visible = false
+ control.itemClicked(modelData)
+ d.updateText(modelData.title)
+ }
+ function updateText(text){
+ d.flagVisible = false
+ control.text = text
+ d.flagVisible = true
+ }
+ function loadData(){
+ var result = []
+ if(items==null){
+ list_view.model = result
+ return
+ }
+ items.map(function(item){
+ if(control.filter(item)){
+ result.push(item)
+ }
+ })
+ list_view.model = result
+ }
+ }
+ onActiveFocusChanged: {
+ if(!activeFocus){
+ control_popup.visible = false
+ }
+ }
+ Popup{
+ id:control_popup
+ y:control.height
+ focus: false
+ padding: 0
+ enter: Transition {
+ NumberAnimation {
+ property: "opacity"
+ from:0
+ to:1
+ duration: FluTheme.enableAnimation ? 83 : 0
+ }
+ }
+ contentItem: FluRectangle{
+ radius: [4,4,4,4]
+ FluShadow{
+ radius: 4
+ }
+ color: FluTheme.dark ? Qt.rgba(51/255,48/255,48/255,1) : Qt.rgba(248/255,250/255,253/255,1)
+ ListView{
+ id:list_view
+ anchors.fill: parent
+ clip: true
+ boundsBehavior: ListView.StopAtBounds
+ ScrollBar.vertical: FluScrollBar {}
+ header: Item{
+ width: control.width
+ height: visible ? 38 : 0
+ visible: list_view.count === 0
+ FluText{
+ text:emptyText
+ anchors{
+ verticalCenter: parent.verticalCenter
+ left: parent.left
+ leftMargin: 10
+ }
+ }
+ }
+ delegate:FluControl{
+ id:item_control
+ height: 38
+ width: control.width
+ onClicked:{
+ d.handleClick(modelData)
+ }
+ background: Rectangle{
+ FluFocusRectangle{
+ visible: item_control.activeFocus
+ radius:4
+ }
+ color: {
+ if(hovered){
+ return FluTheme.dark ? Qt.rgba(63/255,60/255,61/255,1) : Qt.rgba(237/255,237/255,242/255,1)
+ }
+ return FluTheme.dark ? Qt.rgba(51/255,48/255,48/255,1) : Qt.rgba(0,0,0,0)
+ }
+ }
+ contentItem: FluText{
+ text:modelData.title
+ leftPadding: 10
+ rightPadding: 10
+ verticalAlignment : Qt.AlignVCenter
+ }
+ }
+ }
+ }
+ background: Item{
+ id:container
+ implicitWidth: control.width
+ implicitHeight: 38*Math.min(Math.max(list_view.count,1),8)
+ }
+ }
+ onTextChanged: {
+ d.loadData()
+ if(d.flagVisible){
+ var pos = control.mapToItem(null, 0, 0)
+ if(d.window.height>pos.y+control.height+container.implicitHeight){
+ control_popup.y = control.height
+ } else if(pos.y>container.implicitHeight){
+ control_popup.y = -container.implicitHeight
+ } else {
+ control_popup.y = d.window.height-(pos.y+container.implicitHeight)
+ }
+ control_popup.visible = true
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluBadge.qml b/src/FluentUI/Controls/FluBadge.qml
new file mode 100644
index 00000000..d265891a
--- /dev/null
+++ b/src/FluentUI/Controls/FluBadge.qml
@@ -0,0 +1,79 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+Rectangle{
+ property bool isDot: false
+ property bool showZero: false
+ property int count: 0
+ property bool topRight: false
+ id:control
+ color:Qt.rgba(255/255,77/255,79/255,1)
+ width: {
+ if(isDot)
+ return 10
+ if(count<10){
+ return 20
+ }else if(count<100){
+ return 30
+ }
+ return 40
+ }
+ height: {
+ if(isDot)
+ return 10
+ return 20
+ }
+ radius: {
+ if(isDot)
+ return 5
+ return 10
+ }
+ border.width: 1
+ border.color: Qt.rgba(1,1,1,1)
+ anchors{
+ right: {
+ if(parent && topRight)
+ return parent.right
+ return undefined
+ }
+ top: {
+ if(parent && topRight)
+ return parent.top
+ return undefined
+ }
+ rightMargin: {
+ if(parent && topRight){
+ if(isDot){
+ return -2.5
+ }
+ return -(control.width/2)
+ }
+ return 0
+ }
+ topMargin: {
+ if(parent && topRight){
+ if(isDot){
+ return -2.5
+ }
+ return -10
+ }
+ return 0
+ }
+ }
+ visible: {
+ if(showZero)
+ return true
+ return count!==0
+ }
+ FluText{
+ anchors.centerIn: parent
+ color: Qt.rgba(1,1,1,1)
+ visible: !isDot
+ text:{
+ if(count<100)
+ return count
+ return count+"+"
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluBreadcrumbBar.qml b/src/FluentUI/Controls/FluBreadcrumbBar.qml
new file mode 100644
index 00000000..02d1e3a2
--- /dev/null
+++ b/src/FluentUI/Controls/FluBreadcrumbBar.qml
@@ -0,0 +1,92 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import FluentUI
+
+Item {
+ property int textSize: 15
+ property string separator: "/"
+ property var items: []
+ property int spacing: 5
+ signal clickItem(var model)
+ id:control
+ implicitWidth: 300
+ height: 30
+ onItemsChanged: {
+ list_model.clear()
+ list_model.append(items)
+ }
+ ListModel{
+ id:list_model
+ }
+ ListView{
+ id:list_view
+ width: parent.width
+ height: 30
+ orientation: ListView.Horizontal
+ model: list_model
+ clip: true
+ spacing : control.spacing
+ boundsBehavior: ListView.StopAtBounds
+ remove: Transition {
+ NumberAnimation {
+ properties: "opacity"
+ from: 1
+ to: 0
+ duration: FluTheme.enableAnimation ? 83 : 1
+ }
+ }
+ add: Transition {
+ NumberAnimation {
+ properties: "opacity"
+ from: 0
+ to: 1
+ duration: FluTheme.enableAnimation ? 83 : 1
+ }
+ }
+ delegate: Item{
+ height: item_layout.height
+ width: item_layout.width
+ RowLayout{
+ id:item_layout
+ spacing: list_view.spacing
+ height: list_view.height
+
+ FluText{
+ text:model.title
+ Layout.alignment: Qt.AlignVCenter
+ color: {
+ if(item_mouse.pressed){
+ return FluTheme.dark ? Qt.rgba(150/255,150/255,150/235,1) : Qt.rgba(134/255,134/255,134/235,1)
+ }
+ if(item_mouse.containsMouse){
+ return FluTheme.dark ? Qt.rgba(204/255,204/255,204/235,1) : Qt.rgba(92/255,92/255,92/235,1)
+ }
+ return FluTheme.dark ? Qt.rgba(255/255,255/255,255/235,1) : Qt.rgba(26/255,26/255,26/235,1)
+ }
+ MouseArea{
+ id:item_mouse
+ anchors.fill: parent
+ hoverEnabled: true
+ onClicked: {
+ control.clickItem(model)
+ }
+ }
+ }
+
+ FluText{
+ text:control.separator
+ font.pixelSize: control.textSize
+ visible: list_view.count-1 !== index
+ Layout.alignment: Qt.AlignVCenter
+ }
+ }
+ }
+ }
+ function remove(index,count){
+ list_model.remove(index,count)
+ }
+ function count(){
+ return list_model.count
+ }
+}
diff --git a/src/FluentUI/Controls/FluButton.qml b/src/FluentUI/Controls/FluButton.qml
new file mode 100644
index 00000000..dffe4d61
--- /dev/null
+++ b/src/FluentUI/Controls/FluButton.qml
@@ -0,0 +1,65 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Basic
+import FluentUI
+
+Button {
+ property bool disabled: false
+ property string contentDescription: ""
+ 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(246/255,246/255,246/255,1)
+ property color disableColor: FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(244/255,244/255,244/255,1)
+ property color textColor: {
+ if(FluTheme.dark){
+ if(!enabled){
+ return Qt.rgba(131/255,131/255,131/255,1)
+ }
+ if(pressed){
+ return Qt.rgba(162/255,162/255,162/255,1)
+ }
+ return Qt.rgba(1,1,1,1)
+ }else{
+ if(!enabled){
+ return Qt.rgba(160/255,160/255,160/255,1)
+ }
+ if(pressed){
+ return Qt.rgba(96/255,96/255,96/255,1)
+ }
+ return Qt.rgba(0,0,0,1)
+ }
+ }
+ Accessible.role: Accessible.Button
+ Accessible.name: control.text
+ Accessible.description: contentDescription
+ Accessible.onPressAction: control.clicked()
+ id: control
+ enabled: !disabled
+ verticalPadding: 0
+ horizontalPadding:12
+ font:FluTextStyle.Body
+ focusPolicy:Qt.TabFocus
+ background: Rectangle{
+ implicitWidth: 28
+ implicitHeight: 28
+ border.color: FluTheme.dark ? "#505050" : "#DFDFDF"
+ border.width: 1
+ radius: 4
+ color:{
+ if(!enabled){
+ return disableColor
+ }
+ return hovered ? hoverColor :normalColor
+ }
+ FluFocusRectangle{
+ visible: control.activeFocus
+ radius:4
+ }
+ }
+ contentItem: FluText {
+ text: control.text
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ font: control.font
+ color: control.textColor
+ }
+}
diff --git a/src/FluentUI/Controls/FluCalendarPicker.qml b/src/FluentUI/Controls/FluCalendarPicker.qml
new file mode 100644
index 00000000..dbd9d503
--- /dev/null
+++ b/src/FluentUI/Controls/FluCalendarPicker.qml
@@ -0,0 +1,661 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Window
+import FluentUI
+
+Rectangle {
+ property color dividerColor: FluTheme.dark ? Qt.rgba(77/255,77/255,77/255,1) : Qt.rgba(239/255,239/255,239/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 normalColor: FluTheme.dark ? Qt.rgba(61/255,61/255,61/255,1) : Qt.rgba(254/255,254/255,254/255,1)
+ property string text: qsTr("Pick a date")
+ property date from: new Date(1924, 0, 1)
+ property date to: new Date(2124, 11, 31)
+ property var current
+ signal accepted()
+ id:control
+ color: {
+ if(mouse_area.containsMouse){
+ return hoverColor
+ }
+ return normalColor
+ }
+ height: 30
+ width: 120
+ radius: 4
+ border.width: 1
+ border.color: dividerColor
+ MouseArea{
+ id:mouse_area
+ hoverEnabled: true
+ anchors.fill: parent
+ onClicked: {
+ popup.showPopup()
+ }
+ }
+ CalendarModel {
+ id:calender_model
+ from: control.from
+ to: control.to
+ }
+ Item{
+ id:d
+ property var window : Window.window
+ property date displayDate: {
+ if(control.current){
+ return control.current
+ }
+ return new Date()
+ }
+ property date toDay : new Date()
+ property int pageIndex: 0
+ signal nextButton
+ signal previousButton
+ property point yearRing : Qt.point(0,0)
+ }
+ FluText{
+ id:text_date
+ anchors{
+ left: parent.left
+ right: parent.right
+ rightMargin: 30
+ top: parent.top
+ bottom: parent.bottom
+ }
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ text:{
+ if(control.current){
+ return control.current.toLocaleDateString(FluApp.locale,"yyyy/M/d")
+ }
+ return control.text
+ }
+ }
+ FluIcon{
+ iconSource: FluentIcons.Calendar
+ iconSize: 14
+ iconColor: text_date.color
+ anchors{
+ verticalCenter: parent.verticalCenter
+ right: parent.right
+ rightMargin: 12
+ }
+ }
+ Menu{
+ id:popup
+ height: container.height
+ width: container.width
+ modal: true
+ Overlay.modal: Item {}
+ enter: Transition {
+ reversible: true
+ NumberAnimation {
+ property: "opacity"
+ from:0
+ to:1
+ duration: FluTheme.enableAnimation ? 83 : 0
+ }
+ }
+ exit:Transition {
+ NumberAnimation {
+ property: "opacity"
+ from:1
+ to:0
+ duration: FluTheme.enableAnimation ? 83 : 0
+ }
+ }
+ contentItem: Item{
+ clip: true
+ FluArea{
+ id:container
+ width: 300
+ height: 360
+ ColumnLayout {
+ anchors.fill: parent
+ spacing: 0
+ Item{
+ Layout.fillWidth: true
+ Layout.preferredHeight: 50
+ RowLayout{
+ anchors.fill: parent
+ spacing: 10
+ Item{
+ Layout.leftMargin: parent.spacing
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ FluTextButton{
+ width: parent.width
+ anchors.centerIn: parent
+ contentItem: FluText {
+ text: d.displayDate.toLocaleString(FluApp.locale, "MMMM yyyy")
+ verticalAlignment: Text.AlignVCenter
+ }
+ visible: d.pageIndex === 0
+ onClicked: {
+ d.pageIndex = 1
+ }
+ }
+ FluTextButton{
+ width: parent.width
+ anchors.centerIn: parent
+ contentItem: FluText {
+ text: d.displayDate.toLocaleString(FluApp.locale, "yyyy")
+ verticalAlignment: Text.AlignVCenter
+ }
+ visible: d.pageIndex === 1
+ onClicked: {
+ d.pageIndex = 2
+ }
+ }
+ FluTextButton{
+ width: parent.width
+ anchors.centerIn: parent
+ contentItem: FluText {
+ text: "%1-%2".arg(d.yearRing.x).arg(d.yearRing.y)
+ verticalAlignment: Text.AlignVCenter
+ textColor: FluTheme.fontTertiaryColor
+ }
+ visible: d.pageIndex === 2
+ }
+ }
+ FluIconButton{
+ id:icon_up
+ iconSource: FluentIcons.CaretUpSolid8
+ iconSize: 10
+ onClicked: {
+ d.previousButton()
+ }
+ }
+ FluIconButton{
+ id:icon_down
+ iconSource: FluentIcons.CaretDownSolid8
+ iconSize: 10
+ Layout.rightMargin: parent.spacing
+ onClicked: {
+ d.nextButton()
+ }
+ }
+ }
+ FluDivider{
+ width: parent.width
+ height: 1
+ anchors.bottom: parent.bottom
+ }
+ }
+ Item{
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ StackView{
+ id:stack_view
+ anchors.fill: parent
+ initialItem: com_page_one
+ replaceEnter : Transition{
+ OpacityAnimator{
+ from: 0
+ to: 1
+ duration: 88
+ }
+ ScaleAnimator{
+ from: 0.5
+ to: 1
+ duration: 167
+ easing.type: Easing.OutCubic
+ }
+ }
+ replaceExit : Transition{
+ OpacityAnimator{
+ from: 1
+ to: 0
+ duration: 88
+ }
+ ScaleAnimator{
+ from: 1.0
+ to: 0.5
+ duration: 167
+ easing.type: Easing.OutCubic
+ }
+ }
+ }
+ Connections{
+ target: d
+ function onPageIndexChanged(){
+ if(d.pageIndex === 0){
+ stack_view.replace(com_page_one)
+ }
+ if(d.pageIndex === 1){
+ stack_view.replace(com_page_two)
+ }
+ if(d.pageIndex === 2){
+ stack_view.replace(com_page_three)
+ }
+ }
+ }
+ Component{
+ id:com_page_three
+ GridView{
+ id:grid_view
+ cellHeight: 75
+ cellWidth: 75
+ clip: true
+ boundsBehavior: GridView.StopAtBounds
+ ScrollBar.vertical: FluScrollBar {}
+ model: {
+ var fromYear = calender_model.from.getFullYear()
+ var toYear = calender_model.to.getFullYear()
+ return toYear-fromYear+1
+ }
+ highlightRangeMode: GridView.StrictlyEnforceRange
+ onCurrentIndexChanged:{
+ var year = currentIndex + calender_model.from.getFullYear()
+ var start = Math.ceil(year / 10) * 10
+ var end = start+10
+ d.yearRing = Qt.point(start,end)
+ }
+ highlightMoveDuration: 100
+ Component.onCompleted: {
+ grid_view.highlightMoveDuration = 0
+ currentIndex = d.displayDate.getFullYear()-calender_model.from.getFullYear()
+ timer_delay.restart()
+ }
+ Connections{
+ target: d
+ function onNextButton(){
+ grid_view.currentIndex = Math.min(grid_view.currentIndex+16,grid_view.count-1)
+ }
+ function onPreviousButton(){
+ grid_view.currentIndex = Math.max(grid_view.currentIndex-16,0)
+ }
+ }
+ Timer{
+ id:timer_delay
+ interval: 100
+ onTriggered: {
+ grid_view.highlightMoveDuration = 100
+ }
+ }
+ currentIndex: -1
+ delegate: Item{
+ property int year : calender_model.from.getFullYear()+modelData
+ property bool toYear: year === d.toDay.getFullYear()
+ implicitHeight: 75
+ implicitWidth: 75
+ FluControl{
+ id:control_delegate
+ width: 60
+ height: 60
+ anchors.centerIn: parent
+ Rectangle{
+ width: 48
+ height: 48
+ radius: width/2
+ color: {
+ if(toYear){
+ if(control_delegate.pressed){
+ return FluTheme.dark ? Qt.darker(FluTheme.primaryColor,1.2) : Qt.lighter(FluTheme.primaryColor,1.2)
+ }
+ if(control_delegate.hovered){
+ return FluTheme.dark ? Qt.darker(FluTheme.primaryColor,1.1) : Qt.lighter(FluTheme.primaryColor,1.1)
+ }
+ return FluTheme.primaryColor
+ }else{
+ if(control_delegate.pressed){
+ return FluTheme.itemPressColor
+ }
+ if(control_delegate.hovered){
+ return FluTheme.itemHoverColor
+ }
+ return FluColors.Transparent
+ }
+ }
+ anchors.centerIn: parent
+ }
+
+ FluText{
+ text: year
+ anchors.centerIn: parent
+ opacity: {
+ if(year >= d.yearRing.x && year <= d.yearRing.y){
+ return 1
+ }
+ if(control_delegate.hovered){
+ return 1
+ }
+ return 0.3
+ }
+ color: {
+ if(toYear){
+ return FluColors.White
+ }
+ if(control_delegate.pressed){
+ return FluTheme.dark ? FluColors.Grey100 : FluColors.Grey100
+ }
+ if(control_delegate.hovered){
+ return FluTheme.dark ? FluColors.Grey80 : FluColors.Grey120
+ }
+ return FluTheme.dark ? FluColors.White : FluColors.Grey220
+ }
+ }
+ onClicked: {
+ d.displayDate = new Date(year,0,1)
+ d.pageIndex = 1
+ }
+ }
+ }
+ }
+ }
+ Component{
+ id:com_page_two
+
+ ListView{
+ id:listview
+ ScrollBar.vertical: FluScrollBar {}
+ highlightRangeMode: ListView.StrictlyEnforceRange
+ clip: true
+ boundsBehavior: ListView.StopAtBounds
+ spacing: 0
+ highlightMoveDuration: 100
+ model: {
+ var fromYear = calender_model.from.getFullYear()
+ var toYear = calender_model.to.getFullYear()
+ var yearsArray = []
+ for (var i = fromYear; i <= toYear; i++) {
+ yearsArray.push(i)
+ }
+ return yearsArray
+ }
+ currentIndex: -1
+ onCurrentIndexChanged:{
+ var year = model[currentIndex]
+ var month = d.displayDate.getMonth()
+ d.displayDate = new Date(year,month,1)
+ }
+ Connections{
+ target: d
+ function onNextButton(){
+ listview.currentIndex = Math.min(listview.currentIndex+1,listview.count-1)
+ }
+ function onPreviousButton(){
+ listview.currentIndex = Math.max(listview.currentIndex-1,0)
+ }
+ }
+ Component.onCompleted: {
+ listview.highlightMoveDuration = 0
+ currentIndex = model.indexOf(d.displayDate.getFullYear())
+ timer_delay.restart()
+ }
+ Timer{
+ id:timer_delay
+ interval: 100
+ onTriggered: {
+ listview.highlightMoveDuration = 100
+ }
+ }
+ delegate: Item{
+ id:layout_congrol
+ property int year : modelData
+ width: listview.width
+ height: 75*3
+ GridView{
+ anchors.fill: parent
+ cellHeight: 75
+ cellWidth: 75
+ clip: true
+ interactive: false
+ boundsBehavior: GridView.StopAtBounds
+ model: 12
+ delegate: Item{
+ property int month : modelData
+ property bool toMonth: layout_congrol.year === d.toDay.getFullYear() && month === d.toDay.getMonth()
+ implicitHeight: 75
+ implicitWidth: 75
+ FluControl{
+ id:control_delegate
+ width: 60
+ height: 60
+ anchors.centerIn: parent
+ Rectangle{
+ width: 48
+ height: 48
+ radius: width/2
+ color: {
+ if(toMonth){
+ if(control_delegate.pressed){
+ return FluTheme.dark ? Qt.darker(FluTheme.primaryColor,1.2) : Qt.lighter(FluTheme.primaryColor,1.2)
+ }
+ if(control_delegate.hovered){
+ return FluTheme.dark ? Qt.darker(FluTheme.primaryColor,1.1) : Qt.lighter(FluTheme.primaryColor,1.1)
+ }
+ return FluTheme.primaryColor
+ }else{
+ if(control_delegate.pressed){
+ return FluTheme.itemPressColor
+ }
+ if(control_delegate.hovered){
+ return FluTheme.itemHoverColor
+ }
+ return FluColors.Transparent
+ }
+ }
+ anchors.centerIn: parent
+ }
+ FluText{
+ text: new Date(layout_congrol.year,month).toLocaleString(FluApp.locale, "MMMM")
+ anchors.centerIn: parent
+ opacity: {
+ if(layout_congrol.year === d.displayDate.getFullYear()){
+ return 1
+ }
+ if(control_delegate.hovered){
+ return 1
+ }
+ return 0.3
+ }
+ color: {
+ if(toMonth){
+ return FluColors.White
+ }
+ if(control_delegate.pressed){
+ return FluTheme.dark ? FluColors.Grey100 : FluColors.Grey100
+ }
+ if(control_delegate.hovered){
+ return FluTheme.dark ? FluColors.Grey80 : FluColors.Grey120
+ }
+ return FluTheme.dark ? FluColors.White : FluColors.Grey220
+ }
+ }
+ onClicked: {
+ d.displayDate = new Date(layout_congrol.year,month)
+ d.pageIndex = 0
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ Component{
+ id:com_page_one
+ ColumnLayout {
+ DayOfWeekRow {
+ id: dayOfWeekRow
+ locale: FluApp.locale
+ font.bold: false
+ delegate: Label {
+ text: model.shortName
+ font: dayOfWeekRow.font
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+ Layout.column: 1
+ Layout.fillWidth: true
+ }
+ ListView{
+ id:listview
+ property bool isCompleted: false
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ highlightRangeMode: ListView.StrictlyEnforceRange
+ clip: true
+ boundsBehavior: ListView.StopAtBounds
+ spacing: 0
+ highlightMoveDuration: 100
+ currentIndex: -1
+ ScrollBar.vertical: FluScrollBar {}
+ onCurrentIndexChanged:{
+ if(isCompleted){
+ var month = calender_model.monthAt(currentIndex)
+ var year = calender_model.yearAt(currentIndex)
+ d.displayDate = new Date(year,month,1)
+ }
+ }
+ Component.onCompleted: {
+ listview.model = calender_model
+ listview.highlightMoveDuration = 0
+ currentIndex = calender_model.indexOf(d.displayDate)
+ timer_delay.restart()
+ isCompleted = true
+ }
+ Timer{
+ id:timer_delay
+ interval: 100
+ onTriggered: {
+ listview.highlightMoveDuration = 100
+ }
+ }
+ Connections{
+ target: d
+ function onNextButton(){
+ listview.currentIndex = Math.min(listview.currentIndex+1,listview.count-1)
+ }
+ function onPreviousButton(){
+ listview.currentIndex = Math.max(listview.currentIndex-1,0)
+ }
+ }
+ delegate: MonthGrid {
+ id: grid
+ width: listview.width
+ height: listview.height
+ month: model.month
+ year: model.year
+ spacing: 0
+ locale: FluApp.locale
+ delegate: FluControl {
+ required property bool today
+ required property int year
+ required property int month
+ required property int day
+ required property int visibleMonth
+ id: control_delegate
+ visibleMonth: grid.month
+ implicitHeight: 40
+ implicitWidth: 40
+ Rectangle{
+ width: 34
+ height: 34
+ radius: width/2
+ color: {
+ if(today){
+ if(control_delegate.pressed){
+ return FluTheme.dark ? Qt.darker(FluTheme.primaryColor,1.2) : Qt.lighter(FluTheme.primaryColor,1.2)
+ }
+ if(control_delegate.hovered){
+ return FluTheme.dark ? Qt.darker(FluTheme.primaryColor,1.1) : Qt.lighter(FluTheme.primaryColor,1.1)
+ }
+ return FluTheme.primaryColor
+ }else{
+ if(control_delegate.pressed){
+ return FluTheme.itemPressColor
+ }
+ if(control_delegate.hovered){
+ return FluTheme.itemHoverColor
+ }
+ return FluColors.Transparent
+ }
+ }
+ anchors.centerIn: parent
+ }
+ Rectangle{
+ width: 40
+ height: 40
+ border.width: 1
+ anchors.centerIn: parent
+ radius: width/2
+ border.color: FluTheme.primaryColor
+ color: FluColors.Transparent
+ visible: {
+ if(control.current){
+ var y = control.current.getFullYear()
+ var m = control.current.getMonth()
+ var d = control.current.getDate()
+ if(y === year && m === month && d === day){
+ return true
+ }
+ return false
+ }
+ return false
+ }
+ }
+ FluText{
+ text: day
+ opacity: {
+ if(month === grid.month){
+ return 1
+ }
+ if(control_delegate.hovered){
+ return 1
+ }
+ return 0.3
+ }
+ anchors.centerIn: parent
+ color: {
+ if(today){
+ return FluColors.White
+ }
+ if(control_delegate.pressed){
+ return FluTheme.dark ? FluColors.Grey100 : FluColors.Grey100
+ }
+ if(control_delegate.hovered){
+ return FluTheme.dark ? FluColors.Grey80 : FluColors.Grey120
+ }
+ return FluTheme.dark ? FluColors.White : FluColors.Grey220
+ }
+ }
+ onClicked: {
+ control.current = new Date(year,month,day)
+ control.accepted()
+ popup.close()
+ }
+ }
+ background: Item {
+ x: grid.leftPadding
+ y: grid.topPadding
+ width: grid.availableWidth
+ height: grid.availableHeight
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ background: Item{
+ FluShadow{
+ radius: 5
+ }
+ }
+ function showPopup() {
+ var pos = control.mapToItem(null, 0, 0)
+ if(d.window.height>pos.y+control.height+container.height){
+ popup.y = control.height
+ } else if(pos.y>container.height){
+ popup.y = -container.height
+ } else {
+ popup.y = d.window.height-(pos.y+container.height)
+ }
+ popup.x = -(popup.width-control.width)/2
+ popup.open()
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluCarousel.qml b/src/FluentUI/Controls/FluCarousel.qml
new file mode 100644
index 00000000..844b8d9d
--- /dev/null
+++ b/src/FluentUI/Controls/FluCarousel.qml
@@ -0,0 +1,206 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+Item {
+ property bool autoPlay: true
+ property int loopTime: 2000
+ property var model
+ property Component delegate
+ property bool showIndicator: true
+ property int indicatorGravity : Qt.AlignBottom | Qt.AlignHCenter
+ property int indicatorMarginLeft: 0
+ property int indicatorMarginRight: 0
+ property int indicatorMarginTop: 0
+ property int indicatorMarginBottom: 20
+ property int indicatorSpacing: 10
+ property alias indicatorAnchors: layout_indicator.anchors
+ property Component indicatorDelegate : com_indicator
+ id:control
+ width: 400
+ height: 300
+ ListModel{
+ id:content_model
+ }
+ QtObject{
+ id:d
+ property bool flagXChanged: true
+ property bool isAnimEnable: control.autoPlay && list_view.count>3
+ function setData(data){
+ if(!data){
+ return
+ }
+ content_model.clear()
+ content_model.append(data[data.length-1])
+ content_model.append(data)
+ content_model.append(data[0])
+ list_view.highlightMoveDuration = 0
+ list_view.currentIndex = 1
+ list_view.highlightMoveDuration = 250
+ if(d.isAnimEnable){
+ timer_run.restart()
+ }
+ }
+ }
+ ListView{
+ id:list_view
+ anchors.fill: parent
+ snapMode: ListView.SnapOneItem
+ clip: true
+ boundsBehavior: ListView.StopAtBounds
+ model:content_model
+ maximumFlickVelocity: 4 * (list_view.orientation === Qt.Horizontal ? width : height)
+ preferredHighlightBegin: 0
+ preferredHighlightEnd: 0
+ highlightMoveDuration: 0
+ Component.onCompleted: {
+ d.setData(control.model)
+ }
+ interactive: list_view.count>3
+ Connections{
+ target: control
+ function onModelChanged(){
+ d.setData(control.model)
+ }
+ }
+ orientation : ListView.Horizontal
+ delegate: Item{
+ id:item_control
+ width: ListView.view.width
+ height: ListView.view.height
+ property int displayIndex: {
+ if(index === 0)
+ return content_model.count-3
+ if(index === content_model.count-1)
+ return 0
+ return index-1
+ }
+ FluLoader{
+ property int displayIndex : item_control.displayIndex
+ property var model: list_view.model.get(index)
+ anchors.fill: parent
+ sourceComponent: {
+ if(model){
+ return control.delegate
+ }
+ return undefined
+ }
+ }
+ }
+ onMovementEnded:{
+ currentIndex = list_view.contentX/list_view.width
+ if(currentIndex === 0){
+ currentIndex = list_view.count-2
+ }else if(currentIndex === list_view.count-1){
+ currentIndex = 1
+ }
+ d.flagXChanged = false
+ timer_run.restart()
+ }
+ onMovementStarted: {
+ d.flagXChanged = true
+ timer_run.stop()
+ }
+ onContentXChanged: {
+ if(d.flagXChanged){
+ var maxX = Math.min(list_view.width*(currentIndex+1),list_view.count*list_view.width)
+ var minY = Math.max(0,(list_view.width*(currentIndex-1)))
+ if(contentX>=maxX){
+ contentX = maxX
+ }
+ if(contentX<=minY){
+ contentX = minY
+ }
+ }
+ }
+ }
+ Component{
+ id:com_indicator
+ Rectangle{
+ width: 8
+ height: 8
+ radius: 4
+ FluShadow{
+ radius: 4
+ }
+ scale: checked ? 1.2 : 1
+ color: checked ? FluTheme.primaryColor : Qt.rgba(1,1,1,0.7)
+ border.width: mouse_item.containsMouse ? 1 : 0
+ border.color: FluTheme.primaryColor
+ MouseArea{
+ id:mouse_item
+ hoverEnabled: true
+ anchors.fill: parent
+ onClicked: {
+ changedIndex(realIndex)
+ }
+ }
+ }
+ }
+ Row{
+ id:layout_indicator
+ spacing: control.indicatorSpacing
+ anchors{
+ horizontalCenter:(indicatorGravity & Qt.AlignHCenter) ? parent.horizontalCenter : undefined
+ verticalCenter: (indicatorGravity & Qt.AlignVCenter) ? parent.verticalCenter : undefined
+ bottom: (indicatorGravity & Qt.AlignBottom) ? parent.bottom : undefined
+ top: (indicatorGravity & Qt.AlignTop) ? parent.top : undefined
+ left: (indicatorGravity & Qt.AlignLeft) ? parent.left : undefined
+ right: (indicatorGravity & Qt.AlignRight) ? parent.right : undefined
+ bottomMargin: control.indicatorMarginBottom
+ leftMargin: control.indicatorMarginBottom
+ rightMargin: control.indicatorMarginBottom
+ topMargin: control.indicatorMarginBottom
+ }
+ visible: showIndicator
+ Repeater{
+ id:repeater_indicator
+ model: list_view.count
+ FluLoader{
+ property int displayIndex: {
+ if(index === 0)
+ return list_view.count-3
+ if(index === list_view.count-1)
+ return 0
+ return index-1
+ }
+ property int realIndex: index
+ property bool checked: list_view.currentIndex === index
+ sourceComponent: {
+ if(index===0 || index===list_view.count-1)
+ return undefined
+ return control.indicatorDelegate
+ }
+ }
+ }
+ }
+ Timer{
+ id:timer_anim
+ interval: 250
+ onTriggered: {
+ list_view.highlightMoveDuration = 0
+ if(list_view.currentIndex === list_view.count-1){
+ list_view.currentIndex = 1
+ }
+ }
+ }
+ Timer{
+ id:timer_run
+ interval: control.loopTime
+ repeat: d.isAnimEnable
+ onTriggered: {
+ list_view.highlightMoveDuration = 250
+ list_view.currentIndex = list_view.currentIndex+1
+ timer_anim.start()
+ }
+ }
+ function changedIndex(index){
+ d.flagXChanged = true
+ timer_run.stop()
+ list_view.currentIndex = index
+ d.flagXChanged = false
+ if(d.isAnimEnable){
+ timer_run.restart()
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluChart.qml b/src/FluentUI/Controls/FluChart.qml
new file mode 100644
index 00000000..ebc70b15
--- /dev/null
+++ b/src/FluentUI/Controls/FluChart.qml
@@ -0,0 +1,117 @@
+import QtQuick
+import "./../JS/Chart.js" as Chart
+
+Canvas {
+ id: control
+ property string chartType
+ property var chartData
+ property var chartOptions
+ property double chartAnimationProgress: 0.1
+ property int animationEasingType: Easing.InOutExpo
+ property double animationDuration: 300
+ property alias animationRunning: chartAnimator.running
+ signal animationFinished()
+ function animateToNewData()
+ {
+ chartAnimationProgress = 0.1;
+ d.jsChart.update();
+ chartAnimator.restart();
+ }
+ QtObject{
+ id:d
+ property var jsChart: undefined
+ property var memorizedContext
+ property var memorizedData
+ property var memorizedOptions
+ }
+ MouseArea {
+ id: event
+ anchors.fill: control
+ hoverEnabled: true
+ enabled: true
+ property var handler: undefined
+ property QtObject mouseEvent: QtObject {
+ property int left: 0
+ property int top: 0
+ property int x: 0
+ property int y: 0
+ property int clientX: 0
+ property int clientY: 0
+ property string type: ""
+ property var target
+ }
+ function submitEvent(mouse, type) {
+ mouseEvent.type = type
+ mouseEvent.clientX = mouse ? mouse.x : 0;
+ mouseEvent.clientY = mouse ? mouse.y : 0;
+ mouseEvent.x = mouse ? mouse.x : 0;
+ mouseEvent.y = mouse ? mouse.y : 0;
+ mouseEvent.left = 0;
+ mouseEvent.top = 0;
+ mouseEvent.target = control;
+ if(handler) {
+ handler(mouseEvent);
+ }
+ control.requestPaint();
+ }
+ onClicked:
+ (mouse)=> {
+ submitEvent(mouse, "click");
+ }
+ onPositionChanged:
+ (mouse)=> {
+ submitEvent(mouse, "mousemove");
+ }
+ onExited: {
+ submitEvent(undefined, "mouseout");
+ }
+ onEntered: {
+ submitEvent(undefined, "mouseenter");
+ }
+ onPressed:
+ (mouse)=> {
+ submitEvent(mouse, "mousedown");
+ }
+ onReleased:
+ (mouse)=> {
+ submitEvent(mouse, "mouseup");
+ }
+ }
+ PropertyAnimation {
+ id: chartAnimator
+ target: control
+ property: "chartAnimationProgress"
+ alwaysRunToEnd: true
+ to: 1
+ duration: control.animationDuration
+ easing.type: control.animationEasingType
+ onFinished: {
+ control.animationFinished();
+ }
+ }
+ onChartAnimationProgressChanged: {
+ control.requestPaint();
+ }
+ onPaint: {
+ if(control.getContext('2d') !== null && d.memorizedContext !== control.getContext('2d') || d.memorizedData !== control.chartData || d.memorizedOptions !== control.chartOptions) {
+ var ctx = control.getContext('2d');
+ d.jsChart = Chart.build(ctx, {type: control.chartType,data: control.chartData,options: control.chartOptions});
+ d.memorizedData = control.chartData ;
+ d.memorizedContext = control.getContext('2d');
+ d.memorizedOptions = control.chartOptions;
+ d.jsChart.bindEvents(function(newHandler) {event.handler = newHandler;});
+ chartAnimator.start();
+ }
+ d.jsChart.draw(chartAnimationProgress);
+ }
+ onWidthChanged: {
+ if(d.jsChart) {
+ d.jsChart.resize();
+ }
+ }
+ onHeightChanged: {
+ if(d.jsChart) {
+ d.jsChart.resize();
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluCheckBox.qml b/src/FluentUI/Controls/FluCheckBox.qml
new file mode 100644
index 00000000..47c95ee1
--- /dev/null
+++ b/src/FluentUI/Controls/FluCheckBox.qml
@@ -0,0 +1,138 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import FluentUI
+
+Button {
+ property bool disabled: false
+ property string contentDescription: ""
+ property color borderNormalColor: FluTheme.dark ? Qt.rgba(160/255,160/255,160/255,1) : Qt.rgba(136/255,136/255,136/255,1)
+ property color bordercheckedColor: FluTheme.primaryColor
+ property color borderHoverColor: FluTheme.dark ? Qt.rgba(167/255,167/255,167/255,1) : Qt.rgba(135/255,135/255,135/255,1)
+ property color borderDisableColor: FluTheme.dark ? Qt.rgba(82/255,82/255,82/255,1) : Qt.rgba(199/255,199/255,199/255,1)
+ property color borderPressedColor: FluTheme.dark ? Qt.rgba(90/255,90/255,90/255,1) : Qt.rgba(191/255,191/255,191/255,1)
+ property color normalColor: FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(247/255,247/255,247/255,1)
+ property color checkedColor: FluTheme.primaryColor
+ property color hoverColor: FluTheme.dark ? Qt.rgba(72/255,72/255,72/255,1) : Qt.rgba(236/255,236/255,236/255,1)
+ property color checkedHoverColor: FluTheme.dark ? Qt.darker(checkedColor,1.15) : Qt.lighter(checkedColor,1.15)
+ property color checkedPreesedColor: FluTheme.dark ? Qt.darker(checkedColor,1.3) : Qt.lighter(checkedColor,1.3)
+ property color checkedDisableColor: FluTheme.dark ? Qt.rgba(82/255,82/255,82/255,1) : Qt.rgba(199/255,199/255,199/255,1)
+ property color disableColor: FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(253/255,253/255,253/255,1)
+ property real size: 18
+ property alias textColor: btn_text.textColor
+ property bool textRight: true
+ property real textSpacing: 6
+ property bool enableAnimation: FluTheme.enableAnimation
+ property var clickListener : function(){
+ checked = !checked
+ }
+ property bool indeterminate : false
+ id:control
+ enabled: !disabled
+ onClicked: clickListener()
+ onCheckableChanged: {
+ if(checkable){
+ checkable = false
+ }
+ }
+ background: Item{
+ FluFocusRectangle{
+ radius: 4
+ visible: control.activeFocus
+ }
+ }
+ horizontalPadding:0
+ verticalPadding: 0
+ padding: 0
+ Accessible.role: Accessible.Button
+ Accessible.name: control.text
+ Accessible.description: contentDescription
+ Accessible.onPressAction: control.clicked()
+ focusPolicy:Qt.TabFocus
+ contentItem: RowLayout{
+ spacing: control.textSpacing
+ layoutDirection:control.textRight ? Qt.LeftToRight : Qt.RightToLeft
+ Rectangle{
+ width: control.size
+ height: control.size
+ radius: 4
+ border.color: {
+ if(!enabled){
+ return borderDisableColor
+ }
+ if(checked){
+ return bordercheckedColor
+ }
+ if(pressed){
+ return borderPressedColor
+ }
+ if(hovered){
+ return borderHoverColor
+ }
+ return borderNormalColor
+ }
+ border.width: 1
+ color: {
+ if(checked){
+ if(!enabled){
+ return checkedDisableColor
+ }
+ if(pressed){
+ return checkedPreesedColor
+ }
+ if(hovered){
+ return checkedHoverColor
+ }
+ return checkedColor
+ }
+ if(!enabled){
+ return disableColor
+ }
+ if(hovered){
+ return hoverColor
+ }
+ return normalColor
+ }
+ Behavior on color {
+ enabled: control.enableAnimation
+ ColorAnimation{
+ duration: 83
+ }
+ }
+
+ FluIcon {
+ anchors.centerIn: parent
+ iconSource: FluentIcons.CheckboxIndeterminate
+ iconSize: 14
+ visible: indeterminate
+ iconColor: FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
+ Behavior on visible {
+ enabled: control.enableAnimation
+ NumberAnimation{
+ duration: 83
+ }
+ }
+ }
+
+ FluIcon {
+ anchors.centerIn: parent
+ iconSource: FluentIcons.AcceptMedium
+ iconSize: 14
+ visible: checked && !indeterminate
+ iconColor: FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
+ Behavior on visible {
+ enabled: control.enableAnimation
+ NumberAnimation{
+ duration: 83
+ }
+ }
+ }
+ }
+ FluText{
+ id:btn_text
+ text: control.text
+ Layout.alignment: Qt.AlignVCenter
+ visible: text !== ""
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluClip.qml b/src/FluentUI/Controls/FluClip.qml
new file mode 100644
index 00000000..486b9365
--- /dev/null
+++ b/src/FluentUI/Controls/FluClip.qml
@@ -0,0 +1,20 @@
+import QtQuick
+import QtQuick.Controls
+import Qt5Compat.GraphicalEffects
+import FluentUI
+
+FluRectangle {
+ id:control
+ color: "#00000000"
+ layer.enabled: !FluTools.isSoftware()
+ layer.textureSize: Qt.size(control.width*2*Math.ceil(Screen.devicePixelRatio),control.height*2*Math.ceil(Screen.devicePixelRatio))
+ layer.effect: OpacityMask{
+ maskSource: ShaderEffectSource{
+ sourceItem: FluRectangle{
+ radius: control.radius
+ width: control.width
+ height: control.height
+ }
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluColorPicker.qml b/src/FluentUI/Controls/FluColorPicker.qml
new file mode 100644
index 00000000..72728a56
--- /dev/null
+++ b/src/FluentUI/Controls/FluColorPicker.qml
@@ -0,0 +1,589 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Window
+import FluentUI
+
+Button{
+ id:control
+ width: 36
+ height: 36
+ implicitWidth: width
+ implicitHeight: height
+ property color current : Qt.rgba(1,1,1,1)
+ signal accepted()
+ property int colorHandleRadius: 8
+ property string cancelText: "取消"
+ property string okText: "确定"
+ property string titleText: "颜色选择器"
+ property string editText: "编辑颜色"
+ property string redText: "红色"
+ property string greenText: "绿色"
+ property string blueText: "蓝色"
+ property string opacityText: "透明度"
+ background:
+ Rectangle{
+ id:layout_color
+ radius: 5
+ color:"#00000000"
+ border.color: {
+ if(hovered)
+ return FluTheme.primaryColor
+ return FluTheme.dark ? Qt.rgba(100/255,100/255,100/255,1) : Qt.rgba(200/255,200/255,200/255,1)
+ }
+ border.width: 1
+ Rectangle{
+ anchors.fill: parent
+ anchors.margins: 4
+ radius: 5
+ color: control.current
+ }
+ }
+ contentItem: Item{}
+ onClicked: {
+ color_dialog.open()
+ }
+ FluPopup{
+ id:color_dialog
+ implicitWidth: 326
+ implicitHeight: 560
+ closePolicy: Popup.CloseOnEscape
+ Rectangle{
+ id:layout_actions
+ width: parent.width
+ height: 60
+ radius: 5
+ z:999
+ anchors.bottom: parent.bottom
+ color: FluTheme.dark ? Qt.rgba(32/255,32/255,32/255,1) : Qt.rgba(243/255,243/255,243/255,1)
+ RowLayout{
+ anchors
+ {
+ centerIn: parent
+ margins: spacing
+ fill: parent
+ }
+ spacing: 10
+ Item{
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ FluButton{
+ text: control.cancelText
+ width: parent.width
+ anchors.centerIn: parent
+ onClicked: {
+ color_dialog.close()
+ }
+ }
+ }
+ Item{
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ FluFilledButton{
+ text: control.okText
+ width: parent.width
+ anchors.centerIn: parent
+ onClicked: {
+ current = layout_color_hue.colorValue
+ control.accepted()
+ color_dialog.close()
+ }
+ }
+ }
+ }
+ }
+ contentItem: Flickable{
+ implicitWidth: parent.width
+ implicitHeight: Math.min(layout_content.height,560,color_dialog.height)
+ boundsBehavior:Flickable.StopAtBounds
+ contentHeight: layout_content.height + 70
+ contentWidth: width
+ clip: true
+ ScrollBar.vertical: FluScrollBar {}
+ Item{
+ id: layout_content
+ width: parent.width
+ height: childrenRect.height
+ FluText{
+ id: text_titile
+ font: FluTextStyle.Subtitle
+ text: control.titleText
+ anchors{
+ left: parent.left
+ top: parent.top
+ leftMargin: 20
+ topMargin: 20
+ }
+ }
+ Item{
+ id: layout_sb
+ width: 200
+ height: 200
+ anchors{
+ left: parent.left
+ top: text_titile.bottom
+ leftMargin: 12
+ }
+ FluClip{
+ id: layout_color_hue
+ property color colorValue
+ property real xPercent: pickerCursor.x/width
+ property real yPercent: pickerCursor.y/height
+ property real blackPercent: blackCursor.x/(layout_black.width-12)
+ property real opacityPercent: opacityCursor.x/(layout_opacity.width-12)
+ property color opacityColor:{
+ var c = blackColor
+ c = Qt.rgba(c.r,c.g,c.b,opacityPercent)
+ return c
+ }
+ onOpacityColorChanged: {
+ layout_color_hue.colorValue = opacityColor
+ updateColorText(opacityColor)
+ }
+ function updateColorText(color){
+ text_box_r.text = String(Math.floor(color.r*255))
+ text_box_g.text = String(Math.floor(color.g*255))
+ text_box_b.text = String(Math.floor(color.b*255))
+ text_box_a.text = String(Math.floor(color.a*100))
+ var colorString = color.toString().slice(1)
+ if(color.a===1){
+ colorString = "FF"+colorString
+ }
+ text_box_color.text = colorString.toUpperCase()
+ }
+ property color blackColor: {
+ var c = whiteColor
+ c = Qt.rgba(c.r*blackPercent,c.g*blackPercent,c.b*blackPercent,1)
+ return c
+ }
+ property color hueColor: {
+ var v = 1.0-xPercent
+ var c
+ if(0.0 <= v && v < 0.16) {
+ c = Qt.rgba(1.0, 0.0, v/0.16, 1.0)
+ } else if(0.16 <= v && v < 0.33) {
+ c = Qt.rgba(1.0 - (v-0.16)/0.17, 0.0, 1.0, 1.0)
+ } else if(0.33 <= v && v < 0.5) {
+ c = Qt.rgba(0.0, ((v-0.33)/0.17), 1.0, 1.0)
+ } else if(0.5 <= v && v < 0.76) {
+ c = Qt.rgba(0.0, 1.0, 1.0 - (v-0.5)/0.26, 1.0)
+ } else if(0.76 <= v && v < 0.85) {
+ c = Qt.rgba((v-0.76)/0.09, 1.0, 0.0, 1.0)
+ } else if(0.85 <= v && v <= 1.0) {
+ c = Qt.rgba(1.0, 1.0 - (v-0.85)/0.15, 0.0, 1.0)
+ } else {
+ c = Qt.rgba(1.0,0.0,0.0,1.0)
+ }
+ return c
+ }
+ property color whiteColor: {
+ var c = hueColor
+ c = Qt.rgba((1-c.r)*yPercent+c.r,(1-c.g)*yPercent+c.g,(1-c.b)*yPercent+c.b,1.0)
+ return c
+ }
+ function updateColor(){
+ var r = Number(text_box_r.text)/255
+ var g = Number(text_box_g.text)/255
+ var b = Number(text_box_b.text)/255
+ var opacityPercent = Number(text_box_a.text)/100
+ var blackPercent = Math.max(r,g,b)
+ r = r/blackPercent
+ g = g/blackPercent
+ b = b/blackPercent
+ var yPercent = Math.min(r,g,b)
+ if(r === g && r === b){
+ r = 1
+ b = 1
+ g = 1
+ }else{
+ r = (yPercent-r)/(yPercent-1)
+ g = (yPercent-g)/(yPercent-1)
+ b = (yPercent-b)/(yPercent-1)
+ }
+ var xPercent
+ if (r === 1.0 && g === 0.0 && b <= 1.0) {
+ if(b===0.0){
+ xPercent = 0
+ }else{
+ xPercent = 1.0 - b * 0.16
+ }
+ } else if (r <= 1.0 && g === 0.0 && b === 1.0) {
+ xPercent = 1.0 - (1.0 - r) * 0.17 - 0.16
+ } else if (r === 0.0 && g <= 1.0 && b === 1.0) {
+ xPercent = 1.0 - (g * 0.17 + 0.33)
+ } else if (r === 0.0 && g === 1.0 && b <= 1.0) {
+ xPercent = 1.0 - (1.0 - b) * 0.26 - 0.5
+ } else if (r <= 1.0 && g === 1.0 && b === 0.0) {
+ xPercent = 1.0 - (r * 0.09 + 0.76)
+ } else if (r === 1.0 && g <= 1.0 && b === 0.0) {
+ xPercent = 1.0 - (1.0 - g) * 0.15 - 0.85
+ } else {
+ xPercent = 0
+ }
+ pickerCursor.x = xPercent * width
+ pickerCursor.y = yPercent * height
+ blackCursor.x = blackPercent * (layout_black.width-12)
+ opacityCursor.x = opacityPercent * (layout_opacity.width-12)
+ }
+ radius: [4,4,4,4]
+ x: colorHandleRadius
+ y: colorHandleRadius
+ width: parent.width - 2 * colorHandleRadius
+ height: parent.height - 2 * colorHandleRadius
+ Rectangle {
+ anchors.fill: parent
+ gradient: Gradient {
+ orientation: Gradient.Horizontal
+ GradientStop { position: 0.0; color: "#FF0000" }
+ GradientStop { position: 0.16; color: "#FFFF00" }
+ GradientStop { position: 0.33; color: "#00FF00" }
+ GradientStop { position: 0.5; color: "#00FFFF" }
+ GradientStop { position: 0.76; color: "#0000FF" }
+ GradientStop { position: 0.85; color: "#FF00FF" }
+ GradientStop { position: 1.0; color: "#FF0000" }
+ }
+ }
+ Rectangle {
+ anchors.fill: parent
+ gradient: Gradient {
+ GradientStop { position: 1.0; color: "#FFFFFFFF" }
+ GradientStop { position: 0.0; color: "#00000000" }
+ }
+ }
+ Rectangle{
+ radius: 4
+ anchors.fill: parent
+ border.width: 1
+ border.color: FluTheme.dividerColor
+ color:"#00000000"
+ }
+ }
+ Item {
+ id: pickerCursor
+ Rectangle {
+ width: colorHandleRadius*2; height: colorHandleRadius*2
+ radius: colorHandleRadius
+ border.color: "black"; border.width: 2
+ color: "transparent"
+ Rectangle {
+ anchors.fill: parent; anchors.margins: 2;
+ border.color: "white"; border.width: 2
+ radius: width/2
+ color: "transparent"
+ }
+ }
+ }
+ MouseArea {
+ anchors.fill: parent
+ x: colorHandleRadius
+ y: colorHandleRadius
+ preventStealing: true
+ function handleMouse(mouse) {
+ if (mouse.buttons & Qt.LeftButton) {
+ pickerCursor.x = Math.max(0,Math.min(mouse.x - colorHandleRadius,width-2*colorHandleRadius));
+ pickerCursor.y = Math.max(0,Math.min(mouse.y - colorHandleRadius,height-2*colorHandleRadius));
+ }
+ }
+ onPositionChanged:(mouse)=> handleMouse(mouse)
+ onPressed:(mouse)=> handleMouse(mouse)
+ }
+ }
+ FluClip{
+ width: 40
+ height: 200
+ anchors{
+ top: layout_sb.top
+ bottom: layout_sb.bottom
+ left: layout_sb.right
+ topMargin: colorHandleRadius
+ bottomMargin: colorHandleRadius
+ leftMargin: 4
+ }
+ radius: [4,4,4,4]
+ Grid {
+ padding: 0
+ id:target_grid_color
+ anchors.fill: parent
+ rows: height/5+1
+ columns: width/5+1
+ Repeater {
+ model: (target_grid_color.columns-1)*(target_grid_color.rows-1)
+ Rectangle {
+ width: 6
+ height: 6
+ color: (model.index%2 == 0) ? "gray" : "white"
+ }
+ }
+ }
+ Rectangle{
+ anchors.fill: parent
+ color:layout_color_hue.colorValue
+ radius: 4
+ border.width: 1
+ border.color: FluTheme.dividerColor
+ }
+ }
+
+ Column{
+ id:layout_slider_bar
+ spacing: 8
+ anchors{
+ left: parent.left
+ leftMargin: 18
+ right: parent.right
+ rightMargin: 18
+ top: layout_sb.bottom
+ topMargin: 10
+ }
+ Rectangle{
+ id:layout_black
+ radius: 6
+ height: 12
+ width:parent.width
+ gradient: Gradient {
+ orientation:Gradient.Horizontal
+ GradientStop { position: 0.0; color: "#FF000000" }
+ GradientStop { position: 1.0; color: layout_color_hue.hueColor }
+ }
+ Item {
+ id:blackCursor
+ x:layout_black.width-12
+ Rectangle {
+ width: 12
+ height: 12
+ radius: 6
+ border.color: "black"
+ border.width: 2
+ color: "transparent"
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 2
+ border.color: "white"
+ border.width: 2
+ radius: width/2
+ color: "transparent"
+ }
+ }
+ }
+ MouseArea {
+ anchors.fill: parent
+ preventStealing: true
+ function handleMouse(mouse) {
+ if (mouse.buttons & Qt.LeftButton) {
+ blackCursor.x = Math.max(0,Math.min(mouse.x - 6,width-2*6));
+ blackCursor.y = 0
+ }
+ }
+ onPositionChanged:(mouse)=> handleMouse(mouse)
+ onPressed:(mouse)=> handleMouse(mouse)
+ }
+
+ }
+ FluClip{
+ id:layout_opacity
+ height: 12
+ width:parent.width
+ radius: [6,6,6,6]
+ Grid {
+ id:grid_opacity
+ anchors.fill: parent
+ rows: height/4
+ columns: width/4+1
+ clip: true
+ Repeater {
+ model: grid_opacity.columns*grid_opacity.rows
+ Rectangle {
+ width: 4
+ height: 4
+ color: (model.index%2 == 0) ? "gray" : "white"
+ }
+ }
+ }
+ MouseArea{
+ anchors.fill: parent
+ onClicked: {
+ console.debug(grid_opacity.columns,grid_opacity.rows)
+ }
+ }
+ Rectangle{
+ anchors.fill: parent
+ gradient: Gradient {
+ orientation:Gradient.Horizontal
+ GradientStop { position: 0.0; color: "#00000000" }
+ GradientStop { position: 1.0; color: layout_color_hue.blackColor }
+ }
+ }
+ Item {
+ id:opacityCursor
+ x:layout_opacity.width-12
+ Rectangle {
+ width: 12
+ height: 12
+ radius: 6
+ border.color: "black"
+ border.width: 2
+ color: "transparent"
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 2
+ border.color: "white"
+ border.width: 2
+ radius: width/2
+ color: "transparent"
+ }
+ }
+ }
+ MouseArea {
+ id:mouse_opacity
+ anchors.fill: parent
+ preventStealing: true
+ function handleMouse(mouse) {
+ if (mouse.buttons & Qt.LeftButton) {
+ opacityCursor.x = Math.max(0,Math.min(mouse.x - 6,width-2*6));
+ opacityCursor.y = 0
+ }
+ }
+ onPositionChanged:(mouse)=> handleMouse(mouse)
+ onPressed:(mouse)=> handleMouse(mouse)
+ }
+ }
+ }
+
+ Column{
+ anchors{
+ left: parent.left
+ leftMargin: 20
+ top: layout_slider_bar.bottom
+ topMargin: 10
+ right: parent.right
+ rightMargin: 20
+ }
+ spacing: 5
+ Item{
+ width: parent.width
+ height: text_box_color.height
+ FluText{
+ text: control.editText
+ anchors{
+ verticalCenter: parent.verticalCenter
+ left:parent.left
+ }
+ }
+ FluTextBox{
+ id:text_box_color
+ width: 136
+ validator: RegularExpressionValidator {
+ regularExpression: /^[0-9A-F]{8}$/
+ }
+ anchors{
+ right: parent.right
+ }
+ leftPadding: 20
+ FluText{
+ text:"#"
+ anchors{
+ verticalCenter: parent.verticalCenter
+ left: parent.left
+ leftMargin: 5
+ }
+ }
+ onTextEdited: {
+ if(text!==""){
+ var colorString = text_box_color.text.padStart(8,"0")
+ var c = Qt.rgba(
+ parseInt(colorString.substring(2, 4), 16) / 255,
+ parseInt(colorString.substring(4, 6), 16) / 255,
+ parseInt(colorString.substring(6, 8), 16) / 255,
+ parseInt(colorString.substring(0, 2), 16) / 255)
+ layout_color_hue.colorValue = c
+ }
+ }
+ }
+ }
+ Row{
+ spacing: 10
+ FluTextBox{
+ id:text_box_r
+ width: 120
+ validator: RegularExpressionValidator {
+ regularExpression: /^(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$/
+ }
+ onTextEdited: {
+ if(text!==""){
+ layout_color_hue.updateColor()
+ }
+ }
+ }
+ FluText{
+ text: control.redText
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ }
+ Row{
+ spacing: 10
+ FluTextBox{
+ id:text_box_g
+ width: 120
+ validator: RegularExpressionValidator {
+ regularExpression: /^(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$/
+ }
+ onTextEdited: {
+ if(text!==""){
+ layout_color_hue.updateColor()
+ }
+ }
+ }
+ FluText{
+ text: control.greenText
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ }
+ Row{
+ spacing: 10
+ FluTextBox{
+ id:text_box_b
+ width: 120
+ validator: RegularExpressionValidator {
+ regularExpression: /^(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$/
+ }
+ onTextEdited: {
+ if(text!==""){
+ layout_color_hue.updateColor()
+ }
+ }
+ }
+ FluText{
+ text: control.blueText
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ }
+ Row{
+ spacing: 10
+ FluTextBox{
+ id:text_box_a
+ width: 120
+ validator: RegularExpressionValidator {
+ regularExpression: /^(100|[1-9]?\d)$/
+ }
+ FluText{
+ id:text_opacity
+ text:"%"
+ anchors.verticalCenter: parent.verticalCenter
+ x:Math.min(text_box_a.implicitWidth,text_box_a.width)-38
+ }
+ onTextEdited: {
+ if(text!==""){
+ opacityCursor.x = Number(text)/100 * (layout_opacity.width-12)
+ }
+ }
+ }
+ FluText{
+ text: control.opacityText
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluComboBox.qml b/src/FluentUI/Controls/FluComboBox.qml
new file mode 100644
index 00000000..fb7a1665
--- /dev/null
+++ b/src/FluentUI/Controls/FluComboBox.qml
@@ -0,0 +1,146 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Basic
+import FluentUI
+import QtQuick.Templates as T
+
+T.ComboBox {
+ id: control
+ signal commit(string text)
+ property bool disabled: false
+ property color normalColor: FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(254/255,254/255,254/255,1)
+ property color hoverColor: FluTheme.dark ? Qt.rgba(68/255,68/255,68/255,1) : Qt.rgba(251/255,251/255,251/255,1)
+ property color disableColor: FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(252/255,252/255,252/255,1)
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding,
+ implicitIndicatorHeight + topPadding + bottomPadding)
+ font: FluTextStyle.Body
+ leftPadding: padding + (!control.mirrored || !indicator || !indicator.visible ? 0 : indicator.width + spacing)
+ rightPadding: padding + (control.mirrored || !indicator || !indicator.visible ? 0 : indicator.width + spacing)
+ enabled: !disabled
+ delegate: FluItemDelegate {
+ width: ListView.view.width
+ text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) : modelData
+ palette.text: control.palette.text
+ font: control.font
+ palette.highlightedText: control.palette.highlightedText
+ highlighted: control.highlightedIndex === index
+ hoverEnabled: control.hoverEnabled
+ }
+ focusPolicy:Qt.TabFocus
+ indicator: FluIcon {
+ x: control.mirrored ? control.padding : control.width - width - control.padding
+ y: control.topPadding + (control.availableHeight - height) / 2
+ width: 28
+ iconSource:FluentIcons.ChevronDown
+ iconSize: 15
+ opacity: enabled ? 1 : 0.3
+ }
+ contentItem: T.TextField {
+ property bool disabled: !control.editable
+ leftPadding: !control.mirrored ? 10 : control.editable && activeFocus ? 3 : 1
+ rightPadding: control.mirrored ? 10 : control.editable && activeFocus ? 3 : 1
+ topPadding: 6 - control.padding
+ bottomPadding: 6 - control.padding
+ renderType: FluTheme.nativeText ? Text.NativeRendering : Text.QtRendering
+ selectionColor: FluTools.colorAlpha(FluTheme.primaryColor,0.5)
+ selectedTextColor: color
+ text: control.editable ? control.editText : control.displayText
+ enabled: control.editable
+ autoScroll: control.editable
+ font:control.font
+ readOnly: control.down
+ color: {
+ if(control.disabled) {
+ return FluTheme.dark ? Qt.rgba(131/255,131/255,131/255,1) : Qt.rgba(160/255,160/255,160/255,1)
+ }
+ return FluTheme.dark ? Qt.rgba(255/255,255/255,255/255,1) : Qt.rgba(27/255,27/255,27/255,1)
+ }
+ inputMethodHints: control.inputMethodHints
+ validator: control.validator
+ selectByMouse: true
+ verticalAlignment: Text.AlignVCenter
+ leftInset:1
+ topInset:1
+ bottomInset:1
+ rightInset:1
+ background: FluTextBoxBackground{
+ borderWidth: 0
+ inputItem: contentItem
+ }
+ Component.onCompleted: {
+ forceActiveFocus()
+ }
+ Keys.onEnterPressed: (event)=> handleCommit(event)
+ Keys.onReturnPressed:(event)=> handleCommit(event)
+ function handleCommit(event){
+ control.commit(control.editText)
+ accepted()
+ }
+ }
+
+ background: Rectangle {
+ implicitWidth: 140
+ implicitHeight: 32
+ border.color: FluTheme.dark ? "#505050" : "#DFDFDF"
+ border.width: 1
+ visible: !control.flat || control.down
+ radius: 4
+ FluFocusRectangle{
+ visible: control.visualFocus
+ radius:4
+ anchors.margins: -2
+ }
+ color:{
+ if(disabled){
+ return disableColor
+ }
+ return hovered ? hoverColor :normalColor
+ }
+ }
+
+ popup: T.Popup {
+ y: control.height
+ width: control.width
+ height: Math.min(contentItem.implicitHeight, control.Window.height - topMargin - bottomMargin)
+ topMargin: 6
+ bottomMargin: 6
+ modal: true
+ contentItem: ListView {
+ clip: true
+ implicitHeight: contentHeight
+ model: control.delegateModel
+ currentIndex: control.highlightedIndex
+ highlightMoveDuration: 0
+ boundsMovement: Flickable.StopAtBounds
+ T.ScrollIndicator.vertical: ScrollIndicator { }
+ }
+ enter: Transition {
+ NumberAnimation {
+ property: "opacity"
+ from:0
+ to:1
+ duration: FluTheme.enableAnimation ? 83 : 0
+ }
+ }
+ exit:Transition {
+ NumberAnimation {
+ property: "opacity"
+ from:1
+ to:0
+ duration: FluTheme.enableAnimation ? 83 : 0
+ }
+ }
+ background:Rectangle{
+ color:FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(249/255,249/255,249/255,1)
+ border.color: FluTheme.dark ? Window.active ? Qt.rgba(55/255,55/255,55/255,1):Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,229/255,234/255,1)
+ border.width: 1
+ radius: 5
+ FluShadow{
+ radius: 5
+ }
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluContentDialog.qml b/src/FluentUI/Controls/FluContentDialog.qml
new file mode 100644
index 00000000..6c4ae127
--- /dev/null
+++ b/src/FluentUI/Controls/FluContentDialog.qml
@@ -0,0 +1,163 @@
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import QtQuick.Window
+import FluentUI
+
+FluPopup {
+ id: control
+ property string title: ""
+ property string message: ""
+ property string neutralText: qsTr("Close")
+ property string negativeText: qsTr("Cancel")
+ property string positiveText: qsTr("OK")
+ property int messageTextFormart: Text.AutoText
+ property int delayTime: 100
+ property int buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
+ property var contentDelegate: Component{
+ Item{
+ }
+ }
+ property var onNeutralClickListener
+ property var onNegativeClickListener
+ property var onPositiveClickListener
+ signal neutralClicked
+ signal negativeClicked
+ signal positiveClicked
+ implicitWidth: 400
+ implicitHeight: layout_content.height
+ focus: true
+ Component{
+ id:com_message
+ Flickable{
+ id:sroll_message
+ contentHeight: text_message.height
+ contentWidth: width
+ clip: true
+ boundsBehavior:Flickable.StopAtBounds
+ width: parent.width
+ height: message === "" ? 0 : Math.min(text_message.height,300)
+ ScrollBar.vertical: FluScrollBar {}
+ FluText{
+ id:text_message
+ font: FluTextStyle.Body
+ wrapMode: Text.WrapAnywhere
+ text:message
+ width: parent.width
+ topPadding: 4
+ leftPadding: 20
+ rightPadding: 20
+ bottomPadding: 4
+ }
+ }
+ }
+ Rectangle {
+ id:layout_content
+ width: parent.width
+ height: layout_column.childrenRect.height
+ color: 'transparent'
+ radius:5
+ ColumnLayout{
+ id:layout_column
+ width: parent.width
+ FluText{
+ id:text_title
+ font: FluTextStyle.Title
+ text:title
+ topPadding: 20
+ leftPadding: 20
+ rightPadding: 20
+ wrapMode: Text.WrapAnywhere
+ }
+ FluLoader{
+ sourceComponent: com_message
+ Layout.fillWidth: true
+ Layout.preferredHeight: status===Loader.Ready ? item.height : 0
+ }
+ FluLoader{
+ sourceComponent:control.visible ? control.contentDelegate : undefined
+ Layout.fillWidth: true
+ onStatusChanged: {
+ if(status===Loader.Ready){
+ Layout.preferredHeight = item.implicitHeight
+ }else{
+ Layout.preferredHeight = 0
+ }
+ }
+ }
+ Rectangle{
+ id:layout_actions
+ Layout.fillWidth: true
+ Layout.preferredHeight: 60
+ radius: 5
+ color: FluTheme.dark ? Qt.rgba(32/255,32/255,32/255,1) : Qt.rgba(243/255,243/255,243/255,1)
+ RowLayout{
+ anchors
+ {
+ centerIn: parent
+ margins: spacing
+ fill: parent
+ }
+ spacing: 10
+ Item{
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ FluButton{
+ id:neutral_btn
+ visible: control.buttonFlags&FluContentDialogType.NeutralButton
+ text: neutralText
+ width: parent.width
+ anchors.centerIn: parent
+ onClicked: {
+ if(control.onNeutralClickListener){
+ control.onNeutralClickListener()
+ }else{
+ neutralClicked()
+ control.close()
+ }
+ }
+ }
+ }
+ Item{
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ FluButton{
+ id:negative_btn
+ visible: control.buttonFlags&FluContentDialogType.NegativeButton
+ width: parent.width
+ anchors.centerIn: parent
+ text: negativeText
+ onClicked: {
+ if(control.onNegativeClickListener){
+ control.onNegativeClickListener()
+ }else{
+ negativeClicked()
+ control.close()
+ }
+ }
+ }
+ }
+ Item{
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ FluFilledButton{
+ id:positive_btn
+ visible: control.buttonFlags&FluContentDialogType.PositiveButton
+ text: positiveText
+ width: parent.width
+ anchors.centerIn: parent
+ onClicked: {
+ if(control.onPositiveClickListener){
+ control.onPositiveClickListener()
+ }else{
+ positiveClicked()
+ control.close()
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluContentPage.qml b/src/FluentUI/Controls/FluContentPage.qml
new file mode 100644
index 00000000..02815fab
--- /dev/null
+++ b/src/FluentUI/Controls/FluContentPage.qml
@@ -0,0 +1,60 @@
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Window
+import QtQuick.Controls
+import FluentUI
+
+FluPage {
+ property alias title: text_title.text
+ default property alias content: container.data
+ property int leftPadding: 10
+ property int topPadding: 0
+ property int rightPadding: 10
+ property int bottomPadding: 10
+ property alias color: status_view.color
+ property alias statusMode: status_view.statusMode
+ property alias loadingText: status_view.loadingText
+ property alias emptyText:status_view.emptyText
+ property alias errorText:status_view.errorText
+ property alias errorButtonText:status_view.errorButtonText
+ property alias loadingItem :status_view.loadingItem
+ property alias emptyItem : status_view.emptyItem
+ property alias errorItem :status_view.errorItem
+ signal errorClicked
+
+ id:control
+ FluText{
+ id:text_title
+ visible: text !== ""
+ height: visible ? contentHeight : 0
+ font: FluTextStyle.Title
+ anchors{
+ top: parent.top
+ topMargin: control.topPadding
+ left: parent.left
+ right: parent.right
+ leftMargin: control.leftPadding
+ rightMargin: control.rightPadding
+ }
+ }
+ FluStatusLayout{
+ id:status_view
+ color: "#00000000"
+ statusMode: FluStatusLayoutType.Success
+ onErrorClicked: control.errorClicked()
+ anchors{
+ left: parent.left
+ right: parent.right
+ top: text_title.bottom
+ bottom: parent.bottom
+ leftMargin: control.leftPadding
+ rightMargin: control.rightPadding
+ bottomMargin: control.bottomPadding
+ }
+ Item{
+ clip: true
+ id:container
+ anchors.fill: parent
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluControl.qml b/src/FluentUI/Controls/FluControl.qml
new file mode 100644
index 00000000..50f672e4
--- /dev/null
+++ b/src/FluentUI/Controls/FluControl.qml
@@ -0,0 +1,28 @@
+import QtQuick
+import QtQuick.Controls.impl
+import FluentUI
+import QtQuick.Templates as T
+
+T.Button {
+ id: control
+ property string contentDescription: ""
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+ padding: 0
+ horizontalPadding: 0
+ spacing: 0
+ contentItem: Item{}
+ focusPolicy:Qt.TabFocus
+ background: Item{
+ FluFocusRectangle{
+ visible: control.activeFocus
+ radius:8
+ }
+ }
+ Accessible.role: Accessible.Button
+ Accessible.name: control.text
+ Accessible.description: contentDescription
+ Accessible.onPressAction: control.clicked()
+}
diff --git a/src/FluentUI/Controls/FluCopyableText.qml b/src/FluentUI/Controls/FluCopyableText.qml
new file mode 100644
index 00000000..34c1fc7f
--- /dev/null
+++ b/src/FluentUI/Controls/FluCopyableText.qml
@@ -0,0 +1,35 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+TextEdit {
+ property color textColor: FluTheme.dark ? FluColors.White : FluColors.Grey220
+ id:control
+ color: textColor
+ readOnly: true
+ activeFocusOnTab: false
+ activeFocusOnPress: false
+ renderType: FluTheme.nativeText ? Text.NativeRendering : Text.QtRendering
+ padding: 0
+ leftPadding: 0
+ rightPadding: 0
+ topPadding: 0
+ selectByMouse: true
+ selectedTextColor: color
+ bottomPadding: 0
+ selectionColor: FluTools.colorAlpha(FluTheme.primaryColor,0.5)
+ font:FluTextStyle.Body
+ onSelectedTextChanged: {
+ control.forceActiveFocus()
+ }
+ MouseArea{
+ anchors.fill: parent
+ cursorShape: Qt.IBeamCursor
+ acceptedButtons: Qt.RightButton
+ onClicked: control.echoMode !== TextInput.Password && menu.popup()
+ }
+ FluTextBoxMenu{
+ id:menu
+ inputItem: control
+ }
+}
diff --git a/src/FluentUI/Controls/FluDatePicker.qml b/src/FluentUI/Controls/FluDatePicker.qml
new file mode 100644
index 00000000..abfcf47d
--- /dev/null
+++ b/src/FluentUI/Controls/FluDatePicker.qml
@@ -0,0 +1,415 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Window
+import FluentUI
+
+Rectangle {
+ property color dividerColor: FluTheme.dark ? Qt.rgba(77/255,77/255,77/255,1) : Qt.rgba(239/255,239/255,239/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 normalColor: FluTheme.dark ? Qt.rgba(61/255,61/255,61/255,1) : Qt.rgba(254/255,254/255,254/255,1)
+ property bool showYear: true
+ property var current
+ property string yearText: qsTr("Year")
+ property string monthText: qsTr("Month")
+ property string dayText: qsTr("Day")
+ property string cancelText: qsTr("Cancel")
+ property string okText: qsTr("OK")
+ signal accepted()
+ id:control
+ color: {
+ if(mouse_area.containsMouse){
+ return hoverColor
+ }
+ return normalColor
+ }
+ height: 30
+ width: 300
+ radius: 4
+ border.width: 1
+ border.color: dividerColor
+ Component.onCompleted: {
+ if(current){
+ const now = current;
+ var year = text_year.text === control.yearText? now.getFullYear() : Number(text_year.text);
+ var month = text_month.text === control.monthText? now.getMonth() + 1 : Number(text_month.text);
+ var day = text_day.text === control.dayText ? now.getDate() : Number(text_day.text);
+ text_year.text = year
+ text_month.text = month
+ text_day.text = day
+ }
+ }
+ Item{
+ id:d
+ property var window: Window.window
+ property bool changeFlag: true
+ property var rowData: ["","",""]
+ visible: false
+ }
+ MouseArea{
+ id:mouse_area
+ hoverEnabled: true
+ anchors.fill: parent
+ onClicked: {
+ popup.showPopup()
+ }
+ }
+ Rectangle{
+ id:divider_1
+ width: 1
+ x: parent.width/3
+ height: parent.height
+ color: dividerColor
+ visible: showYear
+ }
+ Rectangle{
+ id:divider_2
+ width: 1
+ x: showYear ? parent.width*2/3 : parent.width/2
+ height: parent.height
+ color: dividerColor
+ }
+ FluText{
+ id:text_year
+ anchors{
+ left: parent.left
+ right: divider_1.left
+ top: parent.top
+ bottom: parent.bottom
+ }
+ visible: showYear
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ text:control.yearText
+ }
+ FluText{
+ id:text_month
+ anchors{
+ left: showYear ? divider_1.right : parent.left
+ right: divider_2.left
+ top: parent.top
+ bottom: parent.bottom
+ }
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ text:control.monthText
+ }
+ FluText{
+ id:text_day
+ anchors{
+ left: divider_2.right
+ right: parent.right
+ top: parent.top
+ bottom: parent.bottom
+ }
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ text:control.dayText
+ }
+ Menu{
+ id:popup
+ modal: true
+ width: container.width
+ height: container.height
+ Overlay.modal: Item {}
+ enter: Transition {
+ reversible: true
+ NumberAnimation {
+ property: "opacity"
+ from:0
+ to:1
+ duration: FluTheme.enableAnimation ? 83 : 0
+ }
+ }
+ exit:Transition {
+ NumberAnimation {
+ property: "opacity"
+ from:1
+ to:0
+ duration: FluTheme.enableAnimation ? 83 : 0
+ }
+ }
+ background:Item{
+ FluShadow{
+ radius: 4
+ }
+ }
+ contentItem: Item{
+ clip: true
+ Rectangle{
+ id:container
+ radius: 4
+ width: 300
+ height: 340
+ color: FluTheme.dark ? Qt.rgba(51/255,48/255,48/255,1) : Qt.rgba(248/255,250/255,253/255,1)
+ MouseArea{
+ anchors.fill: parent
+ }
+ FluShadow{
+ radius: 4
+ }
+ RowLayout{
+ id:layout_content
+ spacing: 0
+ width: parent.width
+ height: 300
+ Component{
+ id:list_delegate
+ Item{
+ height:38
+ width:getListView().width
+ function getListView(){
+ if(type === 0)
+ return list_view_1
+ if(type === 1)
+ return list_view_2
+ if(type === 2)
+ return list_view_3
+ }
+ Rectangle{
+ anchors.fill: parent
+ anchors.topMargin: 2
+ anchors.bottomMargin: 2
+ anchors.leftMargin: 5
+ anchors.rightMargin: 5
+ color: {
+ if(getListView().currentIndex === position){
+ return item_mouse.containsMouse ? Qt.lighter(FluTheme.primaryColor,1.1): FluTheme.primaryColor
+ }
+ if(item_mouse.containsMouse){
+ return FluTheme.dark ? Qt.rgba(63/255,60/255,61/255,1) : Qt.rgba(237/255,237/255,242/255,1)
+ }
+ return FluTheme.dark ? Qt.rgba(51/255,48/255,48/255,1) : Qt.rgba(0,0,0,0)
+ }
+ radius: 3
+ MouseArea{
+ id:item_mouse
+ anchors.fill: parent
+ hoverEnabled: true
+ onClicked: {
+ getListView().currentIndex = position
+ if(type === 0){
+ text_year.text = model
+ list_view_2.model = generateMonthArray(1,12)
+ text_month.text = list_view_2.model[list_view_2.currentIndex]
+
+ list_view_3.model = generateMonthDaysArray(list_view_1.model[list_view_1.currentIndex],list_view_2.model[list_view_2.currentIndex])
+ text_day.text = list_view_3.model[list_view_3.currentIndex]
+ }
+ if(type === 1){
+ text_month.text = model
+ list_view_3.model = generateMonthDaysArray(list_view_1.model[list_view_1.currentIndex],list_view_2.model[list_view_2.currentIndex])
+ text_day.text = list_view_3.model[list_view_3.currentIndex]
+
+ }
+ if(type === 2){
+ text_day.text = model
+ }
+ }
+ }
+ FluText{
+ text:model
+ color: {
+ if(getListView().currentIndex === position){
+ if(FluTheme.dark){
+ return Qt.rgba(0,0,0,1)
+ }else{
+ return Qt.rgba(1,1,1,1)
+ }
+ }else{
+ return FluTheme.dark ? "#FFFFFF" : "#1A1A1A"
+ }
+ }
+ anchors.centerIn: parent
+ }
+ }
+ }
+ }
+ ListView{
+ id:list_view_1
+ width: 100
+ height: parent.height
+ boundsBehavior:Flickable.StopAtBounds
+ ScrollBar.vertical: FluScrollBar {}
+ model: generateYearArray(1924,2048)
+ clip: true
+ preferredHighlightBegin: 0
+ preferredHighlightEnd: 0
+ highlightMoveDuration: 0
+ visible: showYear
+ delegate: FluLoader{
+ property var model: modelData
+ property int type:0
+ property int position:index
+ sourceComponent: list_delegate
+ }
+ }
+ Rectangle{
+ width: 1
+ height: parent.height
+ color: dividerColor
+ }
+ ListView{
+ id:list_view_2
+ width: showYear ? 100 : 150
+ height: parent.height
+ clip: true
+ ScrollBar.vertical: FluScrollBar {}
+ preferredHighlightBegin: 0
+ preferredHighlightEnd: 0
+ highlightMoveDuration: 0
+ boundsBehavior:Flickable.StopAtBounds
+ delegate: FluLoader{
+ property var model: modelData
+ property int type:1
+ property int position:index
+ sourceComponent: list_delegate
+ }
+ }
+ Rectangle{
+ width: 1
+ height: parent.height
+ color: dividerColor
+ }
+ ListView{
+ id:list_view_3
+ width: showYear ? 100 : 150
+ height: parent.height
+ clip: true
+ preferredHighlightBegin: 0
+ preferredHighlightEnd: 0
+ highlightMoveDuration: 0
+ ScrollBar.vertical: FluScrollBar {}
+ Layout.alignment: Qt.AlignVCenter
+ boundsBehavior:Flickable.StopAtBounds
+ delegate: FluLoader{
+ property var model: modelData
+ property int type:2
+ property int position:index
+ sourceComponent: list_delegate
+ }
+ }
+ }
+ Rectangle{
+ width: parent.width
+ height: 1
+ anchors.top: layout_content.bottom
+ color: dividerColor
+ }
+ Rectangle{
+ id:layout_actions
+ height: 40
+ radius: 5
+ color: FluTheme.dark ? Qt.rgba(32/255,32/255,32/255,1) : Qt.rgba(243/255,243/255,243/255,1)
+ anchors{
+ bottom:parent.bottom
+ left: parent.left
+ right: parent.right
+ }
+ Item {
+ id:divider
+ width: 1
+ height: parent.height
+ anchors.centerIn: parent
+ }
+ FluButton{
+ anchors{
+ left: parent.left
+ leftMargin: 20
+ rightMargin: 10
+ right: divider.left
+ verticalCenter: parent.verticalCenter
+ }
+ text: control.cancelText
+ onClicked: {
+ popup.close()
+ }
+ }
+ FluFilledButton{
+ anchors{
+ right: parent.right
+ left: divider.right
+ rightMargin: 20
+ leftMargin: 10
+ verticalCenter: parent.verticalCenter
+ }
+ text: control.okText
+ onClicked: {
+ d.changeFlag = false
+ popup.close()
+ const year = text_year.text
+ const month = text_month.text
+ const day = text_day.text
+ const date = new Date()
+ date.setFullYear(parseInt(year));
+ date.setMonth(parseInt(month) - 1);
+ date.setDate(parseInt(day));
+ date.setHours(0);
+ date.setMinutes(0);
+ date.setSeconds(0);
+ current = date
+ control.accepted()
+ }
+ }
+ }
+ }
+ }
+ y:35
+ function showPopup() {
+ d.changeFlag = true
+ d.rowData[0] = text_year.text
+ d.rowData[1] = text_month.text
+ d.rowData[2] = text_day.text
+ const now = new Date();
+ var year = text_year.text === control.yearText? now.getFullYear() : Number(text_year.text);
+ var month = text_month.text === control.monthText? now.getMonth() + 1 : Number(text_month.text);
+ var day = text_day.text === control.dayText ? now.getDate() : Number(text_day.text);
+ list_view_1.currentIndex = list_view_1.model.indexOf(year)
+ text_year.text = year
+ list_view_2.model = generateMonthArray(1,12)
+ list_view_2.currentIndex = list_view_2.model.indexOf(month)
+ text_month.text = month
+ list_view_3.model = generateMonthDaysArray(year,month)
+ list_view_3.currentIndex = list_view_3.model.indexOf(day)
+ text_day.text = day
+ var pos = control.mapToItem(null, 0, 0)
+ if(d.window.height>pos.y+control.height+container.height){
+ popup.y = control.height
+ } else if(pos.y>container.height){
+ popup.y = -container.height
+ } else {
+ popup.y = d.window.height-(pos.y+container.height)
+ }
+ popup.open()
+ }
+ onClosed: {
+ if(d.changeFlag){
+ text_year.text = d.rowData[0]
+ text_month.text = d.rowData[1]
+ text_day.text = d.rowData[2]
+ }
+ }
+ }
+ function generateYearArray(startYear, endYear) {
+ const yearArray = [];
+ for (let year = startYear; year <= endYear; year++) {
+ yearArray.push(year);
+ }
+ return yearArray;
+ }
+ function generateMonthArray(startMonth, endMonth) {
+ const monthArray = [];
+ for (let month = startMonth; month <= endMonth; month++) {
+ monthArray.push(month);
+ }
+ return monthArray;
+ }
+ function generateMonthDaysArray(year, month) {
+ const monthDaysArray = [];
+ const lastDayOfMonth = new Date(year, month, 0).getDate();
+ for (let day = 1; day <= lastDayOfMonth; day++) {
+ monthDaysArray.push(day);
+ }
+ return monthDaysArray;
+ }
+}
diff --git a/src/FluentUI/Controls/FluDivider.qml b/src/FluentUI/Controls/FluDivider.qml
new file mode 100644
index 00000000..3c6748a1
--- /dev/null
+++ b/src/FluentUI/Controls/FluDivider.qml
@@ -0,0 +1,34 @@
+import QtQuick
+import QtQuick.Window
+import FluentUI
+
+Item {
+ id:control
+ property int orientation: Qt.Horizontal
+ property int spacing:0
+ property int size: 1
+ QtObject{
+ id:d
+ property bool isVertical : orientation === Qt.Vertical
+ property int parentHeight: {
+ if(control.parent){
+ return control.parent.height
+ }
+ return control.height
+ }
+ property int parentWidth: {
+ if(control.parent){
+ return control.parent.width
+ }
+ return control.width
+ }
+ }
+ width: d.isVertical ? spacing*2+size : d.parentWidth
+ height: d.isVertical ? d.parentHeight : spacing*2+size
+ FluRectangle{
+ color: FluTheme.dividerColor
+ width: d.isVertical ? size : control.width
+ height: d.isVertical ? control.height : size
+ anchors.centerIn: parent
+ }
+}
diff --git a/src/FluentUI/Controls/FluDropDownButton.qml b/src/FluentUI/Controls/FluDropDownButton.qml
new file mode 100644
index 00000000..5e9f8c3d
--- /dev/null
+++ b/src/FluentUI/Controls/FluDropDownButton.qml
@@ -0,0 +1,96 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Basic
+import QtQuick.Window
+import FluentUI
+
+Button {
+ property bool disabled: false
+ property string contentDescription: ""
+ 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(246/255,246/255,246/255,1)
+ property color disableColor: FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(244/255,244/255,244/255,1)
+ property color textColor: {
+ if(FluTheme.dark){
+ if(!enabled){
+ return Qt.rgba(131/255,131/255,131/255,1)
+ }
+ if(pressed){
+ return Qt.rgba(162/255,162/255,162/255,1)
+ }
+ return Qt.rgba(1,1,1,1)
+ }else{
+ if(!enabled){
+ return Qt.rgba(160/255,160/255,160/255,1)
+ }
+ if(pressed){
+ return Qt.rgba(96/255,96/255,96/255,1)
+ }
+ return Qt.rgba(0,0,0,1)
+ }
+ }
+ property var window : Window.window
+ default property alias contentData: menu.contentData
+ Accessible.role: Accessible.Button
+ Accessible.name: control.text
+ Accessible.description: contentDescription
+ Accessible.onPressAction: control.clicked()
+ id: control
+ rightPadding:35
+ enabled: !disabled
+ focusPolicy:Qt.TabFocus
+ verticalPadding: 0
+ horizontalPadding:12
+ background: Rectangle{
+ implicitWidth: 28
+ implicitHeight: 28
+ border.color: FluTheme.dark ? "#505050" : "#DFDFDF"
+ border.width: 1
+ radius: 4
+ FluFocusRectangle{
+ visible: control.activeFocus
+ radius:8
+ }
+ color:{
+ if(!enabled){
+ return disableColor
+ }
+ return hovered ? hoverColor :normalColor
+ }
+ FluIcon{
+ iconSource:FluentIcons.ChevronDown
+ iconSize: 15
+ anchors{
+ right: parent.right
+ rightMargin: 10
+ verticalCenter: parent.verticalCenter
+ }
+ iconColor:title.color
+ }
+ }
+ contentItem: FluText {
+ id:title
+ text: control.text
+ verticalAlignment: Text.AlignVCenter
+ color: control.textColor
+ }
+ onClicked: {
+ if(menu.count !==0){
+ var pos = control.mapToItem(null, 0, 0)
+ var containerHeight = menu.count*36
+ if(window.height>pos.y+control.height+containerHeight){
+ menu.y = control.height
+ }else if(pos.y>containerHeight){
+ menu.y = -containerHeight
+ }else{
+ menu.y = window.height-(pos.y+containerHeight)
+ }
+ menu.open()
+ }
+ }
+ FluMenu{
+ id:menu
+ modal:true
+ width: control.width
+ }
+}
diff --git a/src/FluentUI/Controls/FluExpander.qml b/src/FluentUI/Controls/FluExpander.qml
new file mode 100644
index 00000000..b2230a70
--- /dev/null
+++ b/src/FluentUI/Controls/FluExpander.qml
@@ -0,0 +1,133 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Window
+import FluentUI
+
+Item {
+ property string headerText: ""
+ property bool expand: false
+ property int contentHeight : 300
+ default property alias content: container.data
+ id:control
+ implicitHeight: Math.max((layout_header.height + layout_container.height),layout_header.height)
+ implicitWidth: 400
+ QtObject{
+ id:d
+ property bool flag: false
+ function toggle(){
+ d.flag = true
+ expand = !expand
+ d.flag = false
+ }
+ }
+ clip: true
+ Rectangle{
+ id:layout_header
+ width: parent.width
+ height: 45
+ radius: 4
+ color: FluTheme.dark ? Window.active ? Qt.rgba(38/255,44/255,54/255,1) : Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1)
+ border.color: FluTheme.dark ? Window.active ? Qt.rgba(55/255,55/255,55/255,1) : Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,229/255,234/255,1)
+ MouseArea{
+ id:control_mouse
+ anchors.fill: parent
+ hoverEnabled: true
+ onClicked: {
+ d.toggle()
+ }
+ }
+ FluText{
+ text: headerText
+ anchors{
+ verticalCenter: parent.verticalCenter
+ left: parent.left
+ leftMargin: 15
+ }
+ }
+ FluIconButton{
+ anchors{
+ verticalCenter: parent.verticalCenter
+ right: parent.right
+ rightMargin: 15
+ }
+ color:{
+ if(control_mouse.containsMouse || hovered){
+ return FluTheme.dark ? Qt.rgba(73/255,73/255,73/255,1) : Qt.rgba(245/255,245/255,245/255,1)
+ }
+ return FluTheme.dark ? Qt.rgba(0,0,0,0) : Qt.rgba(0,0,0,0)
+ }
+ onClicked: {
+ d.toggle()
+ }
+ contentItem: FluIcon{
+ rotation: expand?0:180
+ iconSource:FluentIcons.ChevronUp
+ iconSize: 15
+ Behavior on rotation {
+ enabled: FluTheme.enableAnimation
+ NumberAnimation{
+ duration: 167
+ easing.type: Easing.OutCubic
+ }
+ }
+ }
+ }
+ }
+ Item{
+ id:layout_container
+ anchors{
+ top: layout_header.bottom
+ topMargin: -1
+ left: layout_header.left
+ }
+ visible: contentHeight+container.anchors.topMargin !== 0
+ height: contentHeight+container.anchors.topMargin
+ width: parent.width
+ z:-999
+ Rectangle{
+ id:container
+ anchors.fill: parent
+ radius: 4
+ clip: true
+ color: FluTheme.dark ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1)
+ border.color: FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,229/255,234/255,1)
+ anchors.topMargin: -contentHeight
+ states: [
+ State{
+ name:"expand"
+ when: control.expand
+ PropertyChanges {
+ target: container
+ anchors.topMargin:0
+ }
+ },
+ State{
+ name:"collapsed"
+ when: !control.expand
+ PropertyChanges {
+ target: container
+ anchors.topMargin:-contentHeight
+ }
+ }
+ ]
+ transitions: [
+ Transition {
+ to:"expand"
+ NumberAnimation {
+ properties: "anchors.topMargin"
+ duration: FluTheme.enableAnimation && d.flag ? 167 : 0
+ easing.type: Easing.OutCubic
+ }
+ },
+ Transition {
+ to:"collapsed"
+ NumberAnimation {
+ properties: "anchors.topMargin"
+ duration: FluTheme.enableAnimation && d.flag ? 167 : 0
+ easing.type: Easing.OutCubic
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluFilledButton.qml b/src/FluentUI/Controls/FluFilledButton.qml
new file mode 100644
index 00000000..b83505cf
--- /dev/null
+++ b/src/FluentUI/Controls/FluFilledButton.qml
@@ -0,0 +1,72 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Basic
+import FluentUI
+
+Button {
+ property bool disabled: false
+ property string contentDescription: ""
+ property color normalColor: FluTheme.primaryColor
+ property color hoverColor: FluTheme.dark ? Qt.darker(normalColor,1.1) : Qt.lighter(normalColor,1.1)
+ property color disableColor: FluTheme.dark ? Qt.rgba(82/255,82/255,82/255,1) : Qt.rgba(199/255,199/255,199/255,1)
+ property color pressedColor: FluTheme.dark ? Qt.darker(normalColor,1.2) : Qt.lighter(normalColor,1.2)
+ property color textColor: {
+ if(FluTheme.dark){
+ if(!enabled){
+ return Qt.rgba(173/255,173/255,173/255,1)
+ }
+ return Qt.rgba(0,0,0,1)
+ }else{
+ return Qt.rgba(1,1,1,1)
+ }
+ }
+ Accessible.role: Accessible.Button
+ Accessible.name: control.text
+ Accessible.description: contentDescription
+ Accessible.onPressAction: control.clicked()
+ id: control
+ enabled: !disabled
+ focusPolicy:Qt.TabFocus
+ font:FluTextStyle.Body
+ verticalPadding: 0
+ horizontalPadding:12
+ background: Rectangle{
+ implicitWidth: 28
+ implicitHeight: 28
+ radius: 4
+ FluFocusRectangle{
+ visible: control.visualFocus
+ radius:4
+ }
+ gradient: Gradient {
+ GradientStop { position: 0.33; color: control.enabled ? control.normalColor : Qt.rgba(0,0,0,0) }
+ GradientStop { position: 1.0; color: control.enabled ? Qt.darker(control.normalColor,1.3) : Qt.rgba(0,0,0,0) }
+ }
+ Rectangle{
+ radius: parent.radius
+ anchors{
+ fill: parent
+ topMargin: control.enabled ? 0 : 0
+ leftMargin: control.enabled ? 1 : 0
+ rightMargin: control.enabled ? 1 : 0
+ bottomMargin: control.enabled ? 2 : 0
+ }
+ color:{
+ if(!enabled){
+ return disableColor
+ }
+ if(pressed){
+ return pressedColor
+ }
+ return hovered ? hoverColor :normalColor
+ }
+ }
+ }
+ contentItem: FluText {
+ text: control.text
+ font: control.font
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: control.textColor
+ }
+}
diff --git a/src/FluentUI/Controls/FluFlipView.qml b/src/FluentUI/Controls/FluFlipView.qml
new file mode 100644
index 00000000..1f3012a8
--- /dev/null
+++ b/src/FluentUI/Controls/FluFlipView.qml
@@ -0,0 +1,107 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Basic
+import FluentUI
+
+Item{
+ property bool vertical: false
+ default property alias content : swipe.contentData
+ property alias currentIndex: swipe.currentIndex
+ id:control
+ width: 400
+ height: 300
+ implicitWidth: width
+ implicitHeight: height
+ QtObject{
+ id:d
+ property bool flag: true
+ }
+ MouseArea{
+ anchors.fill: parent
+ preventStealing: true
+ onWheel:
+ (wheel)=>{
+ if(!d.flag)
+ return
+ if (wheel.angleDelta.y > 0){
+ btn_start.clicked()
+ }else{
+ btn_end.clicked()
+ }
+ d.flag = false
+ timer.restart()
+ }
+ }
+ Timer{
+ id:timer
+ interval: 250
+ onTriggered: {
+ d.flag = true
+ }
+ }
+ SwipeView {
+ id:swipe
+ clip: true
+ interactive: false
+ orientation:control.vertical ? Qt.Vertical : Qt.Horizontal
+ anchors.fill: parent
+ }
+ Button{
+ id:btn_start
+ height: vertical ? 20 : 40
+ width: vertical ? 40 : 20
+ anchors{
+ left: vertical ? undefined : parent.left
+ leftMargin: vertical ? undefined : 2
+ verticalCenter: vertical ? undefined : parent.verticalCenter
+ horizontalCenter: !vertical ? undefined : parent.horizontalCenter
+ top: !vertical ? undefined :parent.top
+ topMargin: !vertical ? undefined :2
+ }
+ background: Rectangle{
+ radius: 4
+ color:FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,0.97) : Qt.rgba(237/255,237/255,237/255,0.97)
+ }
+ contentItem:FluIcon{
+ iconSource: vertical ? FluentIcons.CaretUpSolid8 : FluentIcons.CaretLeftSolid8
+ width: 10
+ height: 10
+ iconSize: 10
+ iconColor: btn_start.hovered ? FluColors.Grey220 : FluColors.Grey120
+ anchors.centerIn: parent
+ }
+ visible: swipe.currentIndex !==0
+ onClicked: {
+ swipe.currentIndex = Math.max(swipe.currentIndex - 1, 0)
+ }
+ }
+ Button{
+ id:btn_end
+ height: vertical ? 20 : 40
+ width: vertical ? 40 : 20
+ anchors{
+ right: vertical ? undefined : parent.right
+ rightMargin: vertical ? undefined : 2
+ verticalCenter: vertical ? undefined : parent.verticalCenter
+ horizontalCenter: !vertical ? undefined : parent.horizontalCenter
+ bottom: !vertical ? undefined :parent.bottom
+ bottomMargin: !vertical ? undefined :2
+ }
+ background: Rectangle{
+ radius: 4
+ color:FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,0.97) : Qt.rgba(237/255,237/255,237/255,0.97)
+ }
+ visible: swipe.currentIndex !== swipe.count - 1
+ contentItem:FluIcon{
+ iconSource: vertical ? FluentIcons.CaretDownSolid8 : FluentIcons.CaretRightSolid8
+ width: 10
+ height: 10
+ iconSize: 10
+ iconColor: btn_end.hovered ? FluColors.Grey220 : FluColors.Grey120
+ anchors.centerIn: parent
+ }
+ onClicked: {
+ swipe.currentIndex = Math.min(swipe.currentIndex + 1,swipe.count-1)
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluFocusRectangle.qml b/src/FluentUI/Controls/FluFocusRectangle.qml
new file mode 100644
index 00000000..5c6c9596
--- /dev/null
+++ b/src/FluentUI/Controls/FluFocusRectangle.qml
@@ -0,0 +1,19 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+Item {
+ property int radius: 4
+ id:control
+ anchors.fill: parent
+ Rectangle{
+ width: control.width
+ height: control.height
+ anchors.centerIn: parent
+ color: "#00000000"
+ border.width: 2
+ radius: control.radius
+ border.color: FluTheme.dark ? Qt.rgba(1,1,1,1) : Qt.rgba(0,0,0,1)
+ z: 65535
+ }
+}
diff --git a/src/FluentUI/Controls/FluIcon.qml b/src/FluentUI/Controls/FluIcon.qml
new file mode 100644
index 00000000..edf17b61
--- /dev/null
+++ b/src/FluentUI/Controls/FluIcon.qml
@@ -0,0 +1,19 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+Text {
+ property int iconSource
+ property int iconSize: 20
+ property color iconColor: FluTheme.dark ? "#FFFFFF" : "#000000"
+ id:control
+ font.family: "Segoe Fluent Icons"
+ font.pixelSize: iconSize
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: iconColor
+ text: (String.fromCharCode(iconSource).toString(16))
+ FontLoader{
+ source: "../Font/Segoe_Fluent_Icons.ttf"
+ }
+}
diff --git a/src/FluentUI/Controls/FluIconButton.qml b/src/FluentUI/Controls/FluIconButton.qml
new file mode 100644
index 00000000..b52dcff6
--- /dev/null
+++ b/src/FluentUI/Controls/FluIconButton.qml
@@ -0,0 +1,130 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Controls.Basic
+import FluentUI
+
+Button {
+ display: Button.IconOnly
+ property int iconSize: 20
+ property int iconSource
+ property bool disabled: false
+ property int radius:4
+ property string contentDescription: ""
+ property color hoverColor: FluTheme.itemHoverColor
+ property color pressedColor: FluTheme.itemPressColor
+ property color normalColor: FluTheme.itemNormalColor
+ property color disableColor: FluTheme.itemNormalColor
+ property Component iconDelegate: com_icon
+ property color color: {
+ if(!enabled){
+ return disableColor
+ }
+ if(pressed){
+ return pressedColor
+ }
+ return hovered ? hoverColor : normalColor
+ }
+ property color iconColor: {
+ if(FluTheme.dark){
+ if(!enabled){
+ return Qt.rgba(130/255,130/255,130/255,1)
+ }
+ return Qt.rgba(1,1,1,1)
+ }else{
+ if(!enabled){
+ return Qt.rgba(161/255,161/255,161/255,1)
+ }
+ return Qt.rgba(0,0,0,1)
+ }
+ }
+ property color textColor: FluTheme.fontPrimaryColor
+ Accessible.role: Accessible.Button
+ Accessible.name: control.text
+ Accessible.description: contentDescription
+ Accessible.onPressAction: control.clicked()
+ id:control
+ focusPolicy:Qt.TabFocus
+ padding: 0
+ verticalPadding: 8
+ horizontalPadding: 8
+ enabled: !disabled
+ font:FluTextStyle.Caption
+ background: Rectangle{
+ implicitWidth: 30
+ implicitHeight: 30
+ radius: control.radius
+ color:control.color
+ FluFocusRectangle{
+ visible: control.activeFocus
+ }
+ }
+ Component{
+ id:com_icon
+ FluIcon {
+ id:text_icon
+ font.pixelSize: iconSize
+ iconSize: control.iconSize
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ iconColor: control.iconColor
+ iconSource: control.iconSource
+ }
+ }
+ Component{
+ id:com_row
+ RowLayout{
+ FluLoader{
+ sourceComponent: iconDelegate
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+ visible: display !== Button.TextOnly
+ }
+ FluText{
+ text:control.text
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+ visible: display !== Button.IconOnly
+ color: control.textColor
+ font: control.font
+ }
+ }
+ }
+ Component{
+ id:com_column
+ ColumnLayout{
+ FluLoader{
+ sourceComponent: iconDelegate
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+ visible: display !== Button.TextOnly
+ }
+ FluText{
+ text:control.text
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+ visible: display !== Button.IconOnly
+ color: control.textColor
+ font: control.font
+ }
+ }
+ }
+ contentItem:FluLoader{
+ sourceComponent: {
+ if(display === Button.TextUnderIcon){
+ return com_column
+ }
+ return com_row
+ }
+ }
+ FluTooltip{
+ id:tool_tip
+ visible: {
+ if(control.text === ""){
+ return false
+ }
+ if(control.display !== Button.IconOnly){
+ return false
+ }
+ return hovered
+ }
+ text:control.text
+ delay: 1000
+ }
+}
diff --git a/src/FluentUI/Controls/FluImage.qml b/src/FluentUI/Controls/FluImage.qml
new file mode 100644
index 00000000..dcdc3fc1
--- /dev/null
+++ b/src/FluentUI/Controls/FluImage.qml
@@ -0,0 +1,48 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+Image {
+ property string errorButtonText: qsTr("Reload")
+ property var clickErrorListener : function(){
+ image.source = ""
+ image.source = control.source
+ }
+ property Component errorItem : com_error
+ property Component loadingItem: com_loading
+ id: control
+ FluLoader{
+ anchors.fill: parent
+ sourceComponent: {
+ if(control.status === Image.Loading){
+ return com_loading
+ }else if(control.status == Image.Error){
+ return com_error
+ }else{
+ return undefined
+ }
+ }
+ }
+ Component{
+ id:com_loading
+ Rectangle{
+ color: FluTheme.itemHoverColor
+ FluProgressRing{
+ anchors.centerIn: parent
+ visible: control.status === Image.Loading
+ }
+ }
+ }
+ Component{
+ id:com_error
+ Rectangle{
+ color: FluTheme.itemHoverColor
+ FluFilledButton{
+ text: control.errorButtonText
+ anchors.centerIn: parent
+ visible: control.status === Image.Error
+ onClicked: clickErrorListener()
+ }
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluImageButton.qml b/src/FluentUI/Controls/FluImageButton.qml
new file mode 100644
index 00000000..b028eee4
--- /dev/null
+++ b/src/FluentUI/Controls/FluImageButton.qml
@@ -0,0 +1,18 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+Button{
+ id:control
+ property string normalImage: ""
+ property string hoveredImage: ""
+ property string pushedImage: ""
+ background: Item{
+ implicitHeight: 12
+ implicitWidth: 12
+ BorderImage {
+ anchors.fill: parent
+ source: control.hovered ? (control.pressed ? control.pushedImage : control.hoveredImage ) : control.normalImage
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluInfoBar.qml b/src/FluentUI/Controls/FluInfoBar.qml
new file mode 100644
index 00000000..4eaa185c
--- /dev/null
+++ b/src/FluentUI/Controls/FluInfoBar.qml
@@ -0,0 +1,252 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+FluObject {
+ property var root;
+ property int layoutY: 75
+ id:control
+ FluObject{
+ id:mcontrol
+ property string const_success: "success";
+ property string const_info: "info";
+ property string const_warning: "warning";
+ property string const_error: "error";
+ property int maxWidth: 300;
+ property var screenLayout: null;
+ function create(type,text,duration,moremsg){
+ if(screenLayout){
+ var last = screenLayout.getLastloader();
+ if(last.type === type && last.text === text && moremsg === last.moremsg){
+ last.restart();
+ return;
+ }
+ }
+ initScreenLayout();
+ contentComponent.createObject(screenLayout,{
+ type:type,
+ text:text,
+ duration:duration,
+ moremsg:moremsg,
+ });
+ }
+ function createCustom(itemcomponent,duration){
+ initScreenLayout();
+ if(itemcomponent){
+ contentComponent.createObject(screenLayout,{itemcomponent:itemcomponent,duration:duration});
+ }
+ }
+ function initScreenLayout(){
+ if(screenLayout == null){
+ screenLayout = screenlayoutComponent.createObject(root);
+ screenLayout.y = control.layoutY;
+ screenLayout.z = 100000;
+ }
+ }
+ Component{
+ id:screenlayoutComponent
+ Column{
+ parent: Overlay.overlay
+ z:999
+ spacing: 20
+ width: root.width
+ move: Transition {
+ NumberAnimation {
+ properties: "y"
+ easing.type: Easing.OutCubic
+ duration: FluTheme.enableAnimation ? 333 : 0
+ }
+ }
+ onChildrenChanged: if(children.length === 0) destroy();
+ function getLastloader(){
+ if(children.length > 0){
+ return children[children.length - 1];
+ }
+ return null;
+ }
+ }
+ }
+ Component{
+ id:contentComponent
+ Item{
+ id:content;
+ property int duration: 1500
+ property var itemcomponent
+ property string type
+ property string text
+ property string moremsg
+ width: parent.width;
+ height: loader.height;
+ function close(){
+ content.destroy();
+ }
+ function restart(){
+ delayTimer.restart();
+ }
+ Timer {
+ id:delayTimer
+ interval: duration; running: duration > 0; repeat: duration > 0
+ onTriggered: content.close();
+ }
+ FluLoader{
+ id:loader;
+ x:(parent.width - width) / 2;
+ property var _super: content;
+ scale: item ? 1 : 0;
+ asynchronous: true
+ Behavior on scale {
+ enabled: FluTheme.enableAnimation
+ NumberAnimation {
+ easing.type: Easing.OutCubic
+ duration: 167
+ }
+ }
+ sourceComponent:itemcomponent ? itemcomponent : mcontrol.fluent_sytle;
+ }
+ }
+ }
+ property Component fluent_sytle: Rectangle{
+ width: rowlayout.width + (btn_close.visible ? 30 : 48);
+ height: rowlayout.height + 20;
+ color: {
+ if(FluTheme.dark){
+ switch(_super.type){
+ case mcontrol.const_success: return Qt.rgba(57/255,61/255,27/255,1);
+ case mcontrol.const_warning: return Qt.rgba(67/255,53/255,25/255,1);
+ case mcontrol.const_info: return Qt.rgba(39/255,39/255,39/255,1);
+ case mcontrol.const_error: return Qt.rgba(68/255,39/255,38/255,1);
+ }
+ return Qt.rgba(255,255,255,1)
+ }else{
+ switch(_super.type){
+ case mcontrol.const_success: return "#dff6dd";
+ case mcontrol.const_warning: return "#fff4ce";
+ case mcontrol.const_info: return "#f4f4f4";
+ case mcontrol.const_error: return "#fde7e9";
+ }
+ return "#FFFFFF"
+ }
+ }
+ FluShadow{
+ radius: 4
+ }
+ radius: 4
+ border.width: 1
+ border.color: {
+ if(FluTheme.dark){
+ switch(_super.type){
+ case mcontrol.const_success: return Qt.rgba(56/255,61/255,27/255,1);
+ case mcontrol.const_warning: return Qt.rgba(66/255,53/255,25/255,1);
+ case mcontrol.const_info: return Qt.rgba(38/255,39/255,39/255,1);
+ case mcontrol.const_error: return Qt.rgba(67/255,39/255,38/255,1);
+ }
+ return "#FFFFFF"
+ }else{
+ switch(_super.type){
+ case mcontrol.const_success: return "#d2e8d0";
+ case mcontrol.const_warning: return "#f0e6c2";
+ case mcontrol.const_info: return "#e6e6e6";
+ case mcontrol.const_error: return "#eed9db";
+ }
+ return "#FFFFFF"
+ }
+ }
+ Row{
+ id:rowlayout
+ x:20;
+ y:(parent.height - height) / 2;
+ spacing: 10
+ FluIcon{
+ iconSource:{
+ switch(_super.type){
+ case mcontrol.const_success: return FluentIcons.CompletedSolid;
+ case mcontrol.const_warning: return FluentIcons.InfoSolid;
+ case mcontrol.const_info: return FluentIcons.InfoSolid;
+ case mcontrol.const_error: return FluentIcons.StatusErrorFull;
+ }FluentIcons.StatusErrorFull
+ return FluentIcons.FA_info_circle
+ }
+ iconSize:20
+ iconColor: {
+ if(FluTheme.dark){
+ switch(_super.type){
+ case mcontrol.const_success: return Qt.rgba(108/255,203/255,95/255,1);
+ case mcontrol.const_warning: return Qt.rgba(252/255,225/255,0/255,1);
+ case mcontrol.const_info: return FluTheme.primaryColor;
+ case mcontrol.const_error: return Qt.rgba(255/255,153/255,164/255,1);
+ }
+ return "#FFFFFF"
+ }else{
+ switch(_super.type){
+ case mcontrol.const_success: return "#0f7b0f";
+ case mcontrol.const_warning: return "#9d5d00";
+ case mcontrol.const_info: return "#0066b4";
+ case mcontrol.const_error: return "#c42b1c";
+ }
+ return "#FFFFFF"
+ }
+ }
+ }
+
+ Column{
+ spacing: 5
+ FluText{
+ text:_super.text
+ wrapMode: Text.WrapAnywhere
+ width: Math.min(implicitWidth,mcontrol.maxWidth)
+ }
+ FluText{
+ text: _super.moremsg
+ visible: _super.moremsg
+ wrapMode : Text.WrapAnywhere
+ textColor: FluColors.Grey120
+ width: Math.min(implicitWidth,mcontrol.maxWidth)
+ }
+ }
+
+ FluIconButton{
+ id:btn_close
+ iconSource: FluentIcons.ChromeClose
+ iconSize: 10
+ y:5
+ visible: _super.duration<=0
+ iconColor: {
+ if(FluTheme.dark){
+ switch(_super.type){
+ case mcontrol.const_success: return Qt.rgba(108/255,203/255,95/255,1);
+ case mcontrol.const_warning: return Qt.rgba(252/255,225/255,0/255,1);
+ case mcontrol.const_info: return FluTheme.primaryColor;
+ case mcontrol.const_error: return Qt.rgba(255/255,153/255,164/255,1);
+ }
+ return "#FFFFFF"
+ }else{
+ switch(_super.type){
+ case mcontrol.const_success: return "#0f7b0f";
+ case mcontrol.const_warning: return "#9d5d00";
+ case mcontrol.const_info: return "#0066b4";
+ case mcontrol.const_error: return "#c42b1c";
+ }
+ return "#FFFFFF"
+ }
+ }
+ onClicked: _super.close()
+ }
+ }
+ }
+ }
+ function showSuccess(text,duration=1000,moremsg){
+ mcontrol.create(mcontrol.const_success,text,duration,moremsg ? moremsg : "");
+ }
+ function showInfo(text,duration=1000,moremsg){
+ mcontrol.create(mcontrol.const_info,text,duration,moremsg ? moremsg : "");
+ }
+ function showWarning(text,duration=1000,moremsg){
+ mcontrol.create(mcontrol.const_warning,text,duration,moremsg ? moremsg : "");
+ }
+ function showError(text,duration=1000,moremsg){
+ mcontrol.create(mcontrol.const_error,text,duration,moremsg ? moremsg : "");
+ }
+ function showCustom(itemcomponent,duration=1000){
+ mcontrol.createCustom(itemcomponent,duration);
+ }
+}
diff --git a/src/FluentUI/Controls/FluItemDelegate.qml b/src/FluentUI/Controls/FluItemDelegate.qml
new file mode 100644
index 00000000..265bc3cc
--- /dev/null
+++ b/src/FluentUI/Controls/FluItemDelegate.qml
@@ -0,0 +1,39 @@
+import QtQuick
+import QtQuick.Controls.Basic
+import QtQuick.Templates as T
+import FluentUI
+
+T.ItemDelegate {
+ id: control
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding,
+ implicitIndicatorHeight + topPadding + bottomPadding)
+ padding: 0
+ verticalPadding: 8
+ horizontalPadding: 10
+ icon.color: control.palette.text
+ contentItem:FluText {
+ text: control.text
+ font: control.font
+ color:{
+ if(control.down){
+ return FluTheme.dark ? FluColors.Grey80 : FluColors.Grey120
+ }
+ return FluTheme.dark ? FluColors.White : FluColors.Grey220
+ }
+ }
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 30
+ color:{
+ if(FluTheme.dark){
+ return Qt.rgba(1,1,1,0.05)
+ }else{
+ return Qt.rgba(0,0,0,0.05)
+ }
+ }
+ visible: control.down || control.highlighted || control.visualFocus
+ }
+}
diff --git a/src/FluentUI/Controls/FluLoader.qml b/src/FluentUI/Controls/FluLoader.qml
new file mode 100644
index 00000000..fcf5dae5
--- /dev/null
+++ b/src/FluentUI/Controls/FluLoader.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Loader {
+ Component.onDestruction: sourceComponent = undefined
+}
diff --git a/src/FluentUI/Controls/FluLoadingButton.qml b/src/FluentUI/Controls/FluLoadingButton.qml
new file mode 100644
index 00000000..5b91a281
--- /dev/null
+++ b/src/FluentUI/Controls/FluLoadingButton.qml
@@ -0,0 +1,41 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Basic
+import FluentUI
+
+FluButton {
+ property bool loading: false
+ id: control
+ disabled: loading
+ contentItem: Row{
+ spacing: 6
+ FluText {
+ text: control.text
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ font: control.font
+ color: control.textColor
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ Item{
+ width: control.loading ? 16 : 0
+ height: 16
+ anchors.verticalCenter: parent.verticalCenter
+ visible: Number(width)!==0
+ clip: true
+ Behavior on width {
+ enabled: FluTheme.enableAnimation
+ NumberAnimation{
+ duration: 167
+ easing.type: Easing.OutCubic
+ }
+ }
+ FluProgressRing{
+ width: 16
+ height: 16
+ strokeWidth:3
+ anchors.centerIn: parent
+ }
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluMenu.qml b/src/FluentUI/Controls/FluMenu.qml
new file mode 100644
index 00000000..5090f3ac
--- /dev/null
+++ b/src/FluentUI/Controls/FluMenu.qml
@@ -0,0 +1,59 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Templates as T
+import FluentUI
+
+T.Menu {
+ property bool enableAnimation: true
+ id: control
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ contentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ contentHeight + topPadding + bottomPadding)
+ margins: 0
+ overlap: 1
+ spacing: 0
+ delegate: FluMenuItem { }
+ enter: Transition {
+ NumberAnimation {
+ property: "opacity"
+ from:0
+ to:1
+ duration: FluTheme.enableAnimation && control.enableAnimation ? 83 : 0
+ }
+ }
+ exit:Transition {
+ NumberAnimation {
+ property: "opacity"
+ from:1
+ to:0
+ duration: FluTheme.enableAnimation && control.enableAnimation ? 83 : 0
+ }
+ }
+ contentItem: ListView {
+ implicitHeight: contentHeight
+ model: control.contentModel
+ interactive: Window.window
+ ? contentHeight + control.topPadding + control.bottomPadding > Window.window.height
+ : false
+ clip: true
+ currentIndex: control.currentIndex
+ ScrollIndicator.vertical: ScrollIndicator {}
+ }
+ background: Rectangle {
+ implicitWidth: 150
+ implicitHeight: 36
+ color:FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(240/255,240/255,240/255,1)
+ border.color: FluTheme.dark ? Window.active ? Qt.rgba(55/255,55/255,55/255,1):Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,229/255,234/255,1)
+ border.width: 1
+ radius: 5
+ FluShadow{}
+ }
+ T.Overlay.modal: Rectangle {
+ color: Color.transparent(control.palette.shadow, 0.5)
+ }
+ T.Overlay.modeless: Rectangle {
+ color: Color.transparent(control.palette.shadow, 0.12)
+ }
+}
diff --git a/src/FluentUI/Controls/FluMenuBar.qml b/src/FluentUI/Controls/FluMenuBar.qml
new file mode 100644
index 00000000..9c76ec42
--- /dev/null
+++ b/src/FluentUI/Controls/FluMenuBar.qml
@@ -0,0 +1,21 @@
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls.impl
+
+T.MenuBar {
+ id: control
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ contentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ contentHeight + topPadding + bottomPadding)
+ delegate: FluMenuBarItem { }
+ contentItem: Row {
+ spacing: control.spacing
+ Repeater {
+ model: control.contentModel
+ }
+ }
+ background: Item {
+ implicitHeight: 30
+ }
+}
diff --git a/src/FluentUI/Controls/FluMenuBarItem.qml b/src/FluentUI/Controls/FluMenuBarItem.qml
new file mode 100644
index 00000000..b7e9e175
--- /dev/null
+++ b/src/FluentUI/Controls/FluMenuBarItem.qml
@@ -0,0 +1,61 @@
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls.impl
+import FluentUI
+
+T.MenuBarItem {
+ property bool disabled: false
+ property color textColor: {
+ if(FluTheme.dark){
+ if(disabled){
+ return Qt.rgba(131/255,131/255,131/255,1)
+ }
+ if(pressed){
+ return Qt.rgba(162/255,162/255,162/255,1)
+ }
+ return Qt.rgba(1,1,1,1)
+ }else{
+ if(disabled){
+ return Qt.rgba(160/255,160/255,160/255,1)
+ }
+ if(pressed){
+ return Qt.rgba(96/255,96/255,96/255,1)
+ }
+ return Qt.rgba(0,0,0,1)
+ }
+ }
+ id: control
+ enabled: !disabled
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding,
+ implicitIndicatorHeight + topPadding + bottomPadding)
+ spacing: 6
+ padding: 6
+ leftPadding: 12
+ rightPadding: 16
+ icon.width: 24
+ icon.height: 24
+ icon.color: control.palette.buttonText
+ contentItem: FluText {
+ verticalAlignment: Text.AlignVCenter
+ text: control.text
+ color:control.textColor
+ }
+ background: Rectangle {
+ implicitWidth: 30
+ implicitHeight: 30
+ radius: 3
+ color: {
+ if(control.highlighted){
+ return FluTheme.itemCheckColor
+ }
+ if(control.hovered){
+ return FluTheme.itemHoverColor
+ }
+ return FluTheme.itemNormalColor
+ }
+ }
+}
+
diff --git a/src/FluentUI/Controls/FluMenuItem.qml b/src/FluentUI/Controls/FluMenuItem.qml
new file mode 100644
index 00000000..a1577c6b
--- /dev/null
+++ b/src/FluentUI/Controls/FluMenuItem.qml
@@ -0,0 +1,111 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Controls.impl
+import QtQuick.Templates as T
+import FluentUI
+
+T.MenuItem {
+ property Component iconDelegate : com_icon
+ property int iconSpacing: 5
+ property int iconSource
+ property int iconSize: 16
+ property color textColor: {
+ if(FluTheme.dark){
+ if(!enabled){
+ return Qt.rgba(131/255,131/255,131/255,1)
+ }
+ if(pressed){
+ return Qt.rgba(162/255,162/255,162/255,1)
+ }
+ return Qt.rgba(1,1,1,1)
+ }else{
+ if(!enabled){
+ return Qt.rgba(160/255,160/255,160/255,1)
+ }
+ if(pressed){
+ return Qt.rgba(96/255,96/255,96/255,1)
+ }
+ return Qt.rgba(0,0,0,1)
+ }
+ }
+ id: control
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding,
+ implicitIndicatorHeight + topPadding + bottomPadding)
+ padding: 6
+ spacing: 6
+ icon.width: 24
+ icon.height: 24
+ icon.color: control.palette.windowText
+ height: visible ? implicitHeight : 0
+ font:FluTextStyle.Body
+ Component{
+ id:com_icon
+ FluIcon{
+ id:content_icon
+ iconSize: control.iconSize
+ iconSource:control.iconSource
+ }
+ }
+ contentItem: Item{
+ Row{
+ spacing: control.iconSpacing
+ readonly property real arrowPadding: control.subMenu && control.arrow ? control.arrow.width + control.spacing : 0
+ readonly property real indicatorPadding: control.checkable && control.indicator ? control.indicator.width + control.spacing : 0
+ anchors{
+ verticalCenter: parent.verticalCenter
+ left: parent.left
+ leftMargin: (!control.mirrored ? indicatorPadding : arrowPadding)+5
+ right: parent.right
+ rightMargin: (control.mirrored ? indicatorPadding : arrowPadding)+5
+ }
+ FluLoader{
+ id:loader_icon
+ sourceComponent: iconDelegate
+ anchors.verticalCenter: parent.verticalCenter
+ visible: status === Loader.Ready
+ }
+ FluText {
+ id:content_text
+ text: control.text
+ font: control.font
+ color: control.textColor
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ }
+ }
+ indicator: FluIcon {
+ x: control.mirrored ? control.width - width - control.rightPadding : control.leftPadding
+ y: control.topPadding + (control.availableHeight - height) / 2
+ visible: control.checked
+ iconSource: FluentIcons.CheckMark
+ }
+ arrow: FluIcon {
+ x: control.mirrored ? control.leftPadding : control.width - width - control.rightPadding
+ y: control.topPadding + (control.availableHeight - height) / 2
+ visible: control.subMenu
+ iconSource: FluentIcons.ChevronRightMed
+ }
+ background: Item {
+ implicitWidth: 150
+ implicitHeight: 36
+ x: 1
+ y: 1
+ width: control.width - 2
+ height: control.height - 2
+ Rectangle{
+ anchors.fill: parent
+ anchors.margins: 3
+ radius: 4
+ color:{
+ if(control.highlighted){
+ return FluTheme.itemCheckColor
+ }
+ return FluTheme.itemNormalColor
+ }
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluMenuSeparator.qml b/src/FluentUI/Controls/FluMenuSeparator.qml
new file mode 100644
index 00000000..a687e30a
--- /dev/null
+++ b/src/FluentUI/Controls/FluMenuSeparator.qml
@@ -0,0 +1,19 @@
+import QtQuick
+import QtQuick.Controls.impl
+import QtQuick.Templates as T
+import FluentUI
+
+T.MenuSeparator {
+ id: control
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+ padding: 0
+ verticalPadding: 0
+ contentItem: Rectangle {
+ implicitWidth: 188
+ implicitHeight: 1
+ color: FluTheme.dark ? Qt.rgba(60/255,60/255,60/255,1) : Qt.rgba(210/255,210/255,210/255,1)
+ }
+}
diff --git a/src/FluentUI/Controls/FluMultilineTextBox.qml b/src/FluentUI/Controls/FluMultilineTextBox.qml
new file mode 100644
index 00000000..6d1c3649
--- /dev/null
+++ b/src/FluentUI/Controls/FluMultilineTextBox.qml
@@ -0,0 +1,82 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Basic
+import FluentUI
+
+TextArea{
+ signal commit(string text)
+ property bool disabled: false
+ property color normalColor: FluTheme.dark ? Qt.rgba(255/255,255/255,255/255,1) : Qt.rgba(27/255,27/255,27/255,1)
+ property color disableColor: FluTheme.dark ? Qt.rgba(131/255,131/255,131/255,1) : Qt.rgba(160/255,160/255,160/255,1)
+ property color placeholderNormalColor: FluTheme.dark ? Qt.rgba(210/255,210/255,210/255,1) : Qt.rgba(96/255,96/255,96/255,1)
+ property color placeholderFocusColor: FluTheme.dark ? Qt.rgba(152/255,152/255,152/255,1) : Qt.rgba(141/255,141/255,141/255,1)
+ property color placeholderDisableColor: FluTheme.dark ? Qt.rgba(131/255,131/255,131/255,1) : Qt.rgba(160/255,160/255,160/255,1)
+ property bool isCtrlEnterForNewline: false
+ id:control
+ enabled: !disabled
+ color: {
+ if(!enabled){
+ return disableColor
+ }
+ return normalColor
+ }
+ font:FluTextStyle.Body
+ wrapMode: Text.WrapAnywhere
+ padding: 8
+ leftPadding: padding+4
+ renderType: FluTheme.nativeText ? Text.NativeRendering : Text.QtRendering
+ selectedTextColor: color
+ selectionColor: FluTools.colorAlpha(FluTheme.primaryColor,0.5)
+ placeholderTextColor: {
+ if(!enabled){
+ return placeholderDisableColor
+ }
+ if(focus){
+ return placeholderFocusColor
+ }
+ return placeholderNormalColor
+ }
+ selectByMouse: true
+ width: 240
+ background: FluTextBoxBackground{
+ inputItem: control
+ }
+ Keys.onEnterPressed: (event)=> d.handleCommit(event)
+ Keys.onReturnPressed:(event)=> d.handleCommit(event)
+ QtObject{
+ id:d
+ function handleCommit(event){
+ if(isCtrlEnterForNewline){
+ if(event.modifiers & Qt.ControlModifier){
+ insert(control.cursorPosition, "\n")
+ return
+ }
+ control.commit(control.text)
+ }else{
+ if(event.modifiers & Qt.ControlModifier){
+ control.commit(control.text)
+ return
+ }
+ insert(control.cursorPosition, "\n")
+ }
+ }
+ }
+ MouseArea{
+ anchors.fill: parent
+ cursorShape: Qt.IBeamCursor
+ acceptedButtons: Qt.RightButton
+ onClicked: {
+ if(control.echoMode === TextInput.Password){
+ return
+ }
+ if(control.readOnly && control.text === ""){
+ return
+ }
+ menu.popup()
+ }
+ }
+ FluTextBoxMenu{
+ id:menu
+ inputItem: control
+ }
+}
diff --git a/src/FluentUI/Controls/FluNavigationView.qml b/src/FluentUI/Controls/FluNavigationView.qml
new file mode 100644
index 00000000..a44027a7
--- /dev/null
+++ b/src/FluentUI/Controls/FluNavigationView.qml
@@ -0,0 +1,1344 @@
+import QtQuick
+import QtQuick.Window
+import QtQuick.Controls
+import QtQuick.Controls.Basic
+import QtQuick.Layouts
+import FluentUI
+
+Item {
+ property url logo
+ property string title: ""
+ property FluObject items
+ property FluObject footerItems
+ property int displayMode: FluNavigationViewType.Auto
+ property Component autoSuggestBox
+ property Component actionItem
+ property int topPadding: 0
+ property int pageMode: FluNavigationViewType.Stack
+ property FluMenu navItemRightMenu
+ property FluMenu navItemExpanderRightMenu
+ property int navCompactWidth: 50
+ property int navTopMargin: 0
+ property int cellHeight: 38
+ property int cellWidth: 300
+ property bool hideNavAppBar: false
+ property alias buttonMenu: btn_menu
+ property alias buttonBack: btn_back
+ property alias imageLogo: image_logo
+ signal logoClicked
+ id:control
+ Item{
+ id:d
+ property bool animDisabled:false
+ property var stackItems: []
+ property int displayMode: control.displayMode
+ property bool enableNavigationPanel: false
+ property bool isCompact: d.displayMode === FluNavigationViewType.Compact
+ property bool isMinimal: d.displayMode === FluNavigationViewType.Minimal
+ property bool isCompactAndPanel: d.displayMode === FluNavigationViewType.Compact && d.enableNavigationPanel
+ property bool isCompactAndNotPanel:d.displayMode === FluNavigationViewType.Compact && !d.enableNavigationPanel
+ property bool isMinimalAndPanel: d.displayMode === FluNavigationViewType.Minimal && d.enableNavigationPanel
+ property color itemDisableColor: FluTheme.dark ? Qt.rgba(131/255,131/255,131/255,1) : Qt.rgba(160/255,160/255,160/255,1)
+ onIsCompactAndNotPanelChanged: {
+ collapseAll()
+ }
+ function handleItems(){
+ var _idx = 0
+ var data = []
+ var comEmpty = Qt.createComponent("FluPaneItemEmpty.qml");
+ if(items){
+ for(var i=0;i{
+ if (mouse.button === Qt.RightButton) {
+ if(model.menuDelegate){
+ loader_item_menu.sourceComponent = model.menuDelegate
+ connection_item_menu.target = loader_item_menu.item
+ loader_item_menu.modelData = model
+ loader_item_menu.item.popup();
+ }
+ }
+ }
+ z:-100
+ }
+ onClicked: {
+ if(d.isCompactAndNotPanel && model.children.length > 0){
+ let h = 38*Math.min(Math.max(model.children.length,1),8)
+ let y = mapToItem(control,0,0).y
+ if(h+y>control.height){
+ y = control.height - h
+ }
+ control_popup.showPopup(Qt.point(control.navCompactWidth,y),h,model.children)
+ return
+ }
+ model.isExpand = !model.isExpand
+ }
+ Rectangle{
+ color:Qt.rgba(255/255,77/255,79/255,1)
+ width: 10
+ height: 10
+ radius: 5
+ border.width: 1
+ border.color: Qt.rgba(1,1,1,1)
+ anchors{
+ right: parent.right
+ verticalCenter: parent.verticalCenter
+ rightMargin: 3
+ verticalCenterOffset: -8
+ }
+ visible: {
+ if(!model){
+ return false
+ }
+ if(!model.isExpand){
+ for(var i=0;i{
+ if (mouse.button === Qt.RightButton) {
+ if(model.menuDelegate){
+ loader_item_menu.sourceComponent = model.menuDelegate
+ connection_item_menu.target = loader_item_menu.item
+ loader_item_menu.modelData = model
+ loader_item_menu.item.popup();
+ }
+ }else{
+ item_control.clicked()
+ }
+ }
+ }
+ Rectangle{
+ radius: 4
+ anchors.fill: parent
+ color: {
+ if(!item_control.enabled){
+ return FluTheme.itemNormalColor
+ }
+ if(type===0){
+ if(nav_list.currentIndex === _idx){
+ return FluTheme.itemCheckColor
+ }
+ }else{
+ if(nav_list.currentIndex === (nav_list.count-layout_footer.count+_idx)){
+ return FluTheme.itemCheckColor
+ }
+ }
+ if(item_control.hovered){
+ return FluTheme.itemHoverColor
+ }
+ return FluTheme.itemNormalColor
+ }
+ Component{
+ id:com_icon
+ FluIcon{
+ iconSource: {
+ if(model&&model.icon){
+ return model.icon
+ }
+ return 0
+ }
+ color: {
+ if(!item_control.enabled){
+ return d.itemDisableColor
+ }
+ return FluTheme.dark ? "#FFFFFF" : "#000000"
+ }
+ iconSize: 15
+ }
+ }
+ Item{
+ id:item_icon
+ height: 30
+ width: visible ? 30 : 8
+ visible: {
+ if(model){
+ return model.iconVisible
+ }
+ return true
+ }
+ anchors{
+ left:parent.left
+ verticalCenter: parent.verticalCenter
+ leftMargin: d.isCompactAndNotPanel ? (parent.width - 30)/2 : 3
+ }
+ FluLoader{
+ anchors.centerIn: parent
+ sourceComponent: {
+ if(model&&model.iconDelegate){
+ return model.iconDelegate
+ }
+ return com_icon
+ }
+ }
+ }
+ FluText{
+ id:item_title
+ text:{
+ if(model){
+ if(!item_icon.visible && d.isCompactAndNotPanel){
+ return model.title[0]
+ }
+ return model.title
+ }
+ return ""
+ }
+ visible: {
+ if(d.isCompactAndNotPanel){
+ if(item_icon.visible){
+ return false
+ }
+ return true
+ }
+ return true
+ }
+ elide: Text.ElideRight
+ color:{
+ if(!item_control.enabled){
+ return d.itemDisableColor
+ }
+ if(item_mouse.pressed){
+ return FluTheme.dark ? FluColors.Grey80 : FluColors.Grey120
+ }
+ return FluTheme.dark ? FluColors.White : FluColors.Grey220
+ }
+ anchors{
+ verticalCenter: parent.verticalCenter
+ left:item_icon.right
+ right: item_dot_loader.left
+ }
+ }
+ FluLoader{
+ id:item_edit_loader
+ anchors{
+ top: parent.top
+ bottom: parent.bottom
+ left: item_title.left
+ right: item_title.right
+ rightMargin: 8
+ }
+ sourceComponent: {
+ if(d.isCompact){
+ return undefined
+ }
+ if(!model){
+ return undefined
+ }
+ return model.showEdit ? model.editDelegate : undefined
+ }
+ onStatusChanged: {
+ if(status === FluLoader.Ready){
+ item.forceActiveFocus()
+ item_connection_edit_focus.target = item
+ }
+ }
+ Connections{
+ id:item_connection_edit_focus
+ ignoreUnknownSignals:true
+ function onActiveFocusChanged(focus){
+ if(focus === false){
+ model.showEdit = false
+ }
+ }
+ function onCommit(text){
+ model.title = text
+ model.showEdit = false
+ }
+ }
+ }
+ FluLoader{
+ id:item_dot_loader
+ property bool isDot: (item_dot_loader.item&&item_dot_loader.item.isDot)
+ anchors{
+ right: parent.right
+ verticalCenter: parent.verticalCenter
+ rightMargin: isDot ? 3 : 10
+ verticalCenterOffset: isDot ? -8 : 0
+ }
+ sourceComponent: {
+ if(model&&model.infoBadge){
+ return model.infoBadge
+ }
+ return undefined
+ }
+ Connections{
+ target: d
+ function onIsCompactAndNotPanelChanged(){
+ if(item_dot_loader.item){
+ item_dot_loader.item.isDot = d.isCompactAndNotPanel
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ Item {
+ id:nav_app_bar
+ width: parent.width
+ height: visible ? 40 : 0
+ anchors{
+ top: parent.top
+ topMargin: control.topPadding
+ }
+ visible: !control.hideNavAppBar
+ z:999
+ RowLayout{
+ height:parent.height
+ spacing: 0
+ FluIconButton{
+ id:btn_back
+ iconSource: FluentIcons.ChromeBack
+ Layout.leftMargin: 5
+ Layout.alignment: Qt.AlignVCenter
+ disabled: {
+ return d.stackItems.length <= 1
+ }
+ iconSize: 13
+ onClicked: {
+ d.stackItems = d.stackItems.slice(0, -1)
+ var item = d.stackItems[d.stackItems.length-1]
+ if(item._idx<(nav_list.count - layout_footer.count)){
+ layout_footer.currentIndex = -1
+ }else{
+ layout_footer.currentIndex = item._idx-(nav_list.count-layout_footer.count)
+ }
+ nav_list.currentIndex = item._idx
+ if(pageMode === FluNavigationViewType.Stack){
+ var nav_stack = loader_content.item.navStack()
+ var nav_stack2 = loader_content.item.navStack2()
+ nav_stack.pop()
+ if(nav_stack.currentItem.launchMode === FluPageType.SingleInstance){
+ var url = nav_stack.currentItem.url
+ var pageIndex = -1
+ for(var i=0;i children
+ id:control
+}
diff --git a/src/FluentUI/Controls/FluPage.qml b/src/FluentUI/Controls/FluPage.qml
new file mode 100644
index 00000000..87420acd
--- /dev/null
+++ b/src/FluentUI/Controls/FluPage.qml
@@ -0,0 +1,43 @@
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import QtQuick.Window
+import FluentUI
+
+Item {
+ property int launchMode: FluPageType.SingleTop
+ property bool animDisabled: false
+ property string url : ""
+ signal animationEnd()
+ id: control
+ opacity: visible
+ visible: false
+ StackView.onRemoved: destroy()
+ Behavior on opacity{
+ enabled: !animDisabled && FluTheme.enableAnimation
+ NumberAnimation{
+ duration: 167
+ }
+ }
+ transform: Translate {
+ y: control.visible ? 0 : 80
+ Behavior on y{
+ enabled: !animDisabled && FluTheme.enableAnimation
+ NumberAnimation{
+ duration: 167
+ easing.type: Easing.OutCubic
+ }
+ }
+ }
+ Component.onCompleted: {
+ visible = true
+ timer.restart()
+ }
+ Timer{
+ id:timer
+ interval: !animDisabled && FluTheme.enableAnimation ? 200 : 0
+ onTriggered: {
+ control.animationEnd()
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluPagination.qml b/src/FluentUI/Controls/FluPagination.qml
new file mode 100644
index 00000000..37f6807d
--- /dev/null
+++ b/src/FluentUI/Controls/FluPagination.qml
@@ -0,0 +1,100 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import FluentUI
+
+Item {
+ signal requestPage(int page,int count)
+ property string previousText: qsTr("")
+ property int pageCurrent: 0
+ property int itemCount: 0
+ property int pageButtonCount: 5
+ property int pageCount: itemCount>0?Math.ceil(itemCount/__itemPerPage):0
+ property int __itemPerPage: 10
+ property int __pageButtonHalf: Math.floor(pageButtonCount/2)+1
+ id: control
+ implicitHeight: 40
+ implicitWidth: content.width
+ Row{
+ id: content
+ height: control.height
+ spacing: 10
+ padding: 10
+ FluToggleButton{
+ visible: control.pageCount>1
+ disabled: control.pageCurrent<=1
+ text:control.previousText
+ clickListener:function() {
+ control.calcNewPage(control.pageCurrent-1);
+ }
+ }
+ Row{
+ spacing: 5
+ FluToggleButton{
+ property int pageNumber:1
+ visible: control.pageCount>0
+ checked: pageNumber === control.pageCurrent
+ text:String(pageNumber)
+ clickListener:function() {
+ control.calcNewPage(pageNumber);
+ }
+ }
+ FluText{
+ visible: (control.pageCount>control.pageButtonCount&&
+ control.pageCurrent>control.__pageButtonHalf)
+ text: "..."
+ }
+ Repeater{
+ id: button_repeator
+ model: (control.pageCount<2)?0:(control.pageCount>=control.pageButtonCount)?(control.pageButtonCount-2):(control.pageCount-2)
+ delegate:FluToggleButton{
+ property int pageNumber: {
+ return (control.pageCurrent<=control.__pageButtonHalf)
+ ?(2+index)
+ :(control.pageCount-control.pageCurrent<=control.pageButtonCount-control.__pageButtonHalf)
+ ?(control.pageCount-button_repeator.count+index)
+ :(control.pageCurrent+2+index-control.__pageButtonHalf)
+ }
+ text:String(pageNumber)
+ checked: pageNumber === control.pageCurrent
+ clickListener:function(){
+ control.calcNewPage(pageNumber);
+ }
+ }
+ }
+ FluText{
+ visible: (control.pageCount>control.pageButtonCount&&
+ control.pageCount-control.pageCurrent>control.pageButtonCount-control.__pageButtonHalf)
+ text: "..."
+ }
+ FluToggleButton{
+ property int pageNumber:control.pageCount
+ visible: control.pageCount>1
+ checked: pageNumber === control.pageCurrent
+ text:String(pageNumber)
+ clickListener:function(){
+ control.calcNewPage(pageNumber);
+ }
+ }
+ }
+ FluToggleButton{
+ visible: control.pageCount>1
+ disabled: control.pageCurrent>=control.pageCount
+ text:control.nextText
+ clickListener:function() {
+ control.calcNewPage(control.pageCurrent+1);
+ }
+ }
+ }
+ function calcNewPage(page)
+ {
+ if(!page)
+ return
+ let page_num=Number(page)
+ if(page_num<1||page_num>control.pageCount||page_num===control.pageCurrent)
+ return
+ control.pageCurrent=page_num
+ control.requestPage(page_num,control.__itemPerPage)
+ }
+}
diff --git a/src/FluentUI/Controls/FluPaneItem.qml b/src/FluentUI/Controls/FluPaneItem.qml
new file mode 100644
index 00000000..d2214015
--- /dev/null
+++ b/src/FluentUI/Controls/FluPaneItem.qml
@@ -0,0 +1,25 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+QtObject {
+ readonly property string key : FluTools.uuid()
+ property int _idx
+ property var _ext
+ property var _parent
+ property bool visible: true
+ property string title
+ property var url
+ property bool disabled: false
+ property int icon
+ property bool iconVisible: true
+ property Component infoBadge
+ property int count: 0
+ property var onTapListener
+ property Component iconDelegate
+ property Component menuDelegate
+ property Component editDelegate
+ property var extra
+ property bool showEdit
+ signal tap
+}
diff --git a/src/FluentUI/Controls/FluPaneItemEmpty.qml b/src/FluentUI/Controls/FluPaneItemEmpty.qml
new file mode 100644
index 00000000..0f12e384
--- /dev/null
+++ b/src/FluentUI/Controls/FluPaneItemEmpty.qml
@@ -0,0 +1,11 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+QtObject {
+ readonly property string key : FluTools.uuid()
+ property int _idx
+ property var _ext
+ property var _parent
+ property bool visible: true
+}
diff --git a/src/FluentUI/Controls/FluPaneItemExpander.qml b/src/FluentUI/Controls/FluPaneItemExpander.qml
new file mode 100644
index 00000000..93c2e648
--- /dev/null
+++ b/src/FluentUI/Controls/FluPaneItemExpander.qml
@@ -0,0 +1,18 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+FluObject {
+ readonly property string key : FluTools.uuid()
+ property int _idx
+ property bool visible: true
+ property string title
+ property var icon
+ property bool disabled: false
+ property bool iconVisible: true
+ property bool isExpand: false
+ property bool showEdit
+ property Component iconDelegate
+ property Component menuDelegate
+ property Component editDelegate
+}
diff --git a/src/FluentUI/Controls/FluPaneItemHeader.qml b/src/FluentUI/Controls/FluPaneItemHeader.qml
new file mode 100644
index 00000000..6918097f
--- /dev/null
+++ b/src/FluentUI/Controls/FluPaneItemHeader.qml
@@ -0,0 +1,11 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+QtObject {
+ readonly property string key : FluTools.uuid()
+ property int _idx
+ property bool visible: true
+ property string title
+ property var parent
+}
diff --git a/src/FluentUI/Controls/FluPaneItemSeparator.qml b/src/FluentUI/Controls/FluPaneItemSeparator.qml
new file mode 100644
index 00000000..23433b68
--- /dev/null
+++ b/src/FluentUI/Controls/FluPaneItemSeparator.qml
@@ -0,0 +1,12 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+QtObject {
+ readonly property string key : FluTools.uuid()
+ property int _idx
+ property bool visible: true
+ property var parent
+ property real spacing
+ property int size:1
+}
diff --git a/src/FluentUI/Controls/FluPasswordBox.qml b/src/FluentUI/Controls/FluPasswordBox.qml
new file mode 100644
index 00000000..e493a95c
--- /dev/null
+++ b/src/FluentUI/Controls/FluPasswordBox.qml
@@ -0,0 +1,73 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Basic
+import FluentUI
+
+TextField{
+ signal commit(string text)
+ property bool disabled: false
+ property int iconSource: 0
+ property color normalColor: FluTheme.dark ? Qt.rgba(255/255,255/255,255/255,1) : Qt.rgba(27/255,27/255,27/255,1)
+ property color disableColor: FluTheme.dark ? Qt.rgba(131/255,131/255,131/255,1) : Qt.rgba(160/255,160/255,160/255,1)
+ property color placeholderNormalColor: FluTheme.dark ? Qt.rgba(210/255,210/255,210/255,1) : Qt.rgba(96/255,96/255,96/255,1)
+ property color placeholderFocusColor: FluTheme.dark ? Qt.rgba(152/255,152/255,152/255,1) : Qt.rgba(141/255,141/255,141/255,1)
+ property color placeholderDisableColor: FluTheme.dark ? Qt.rgba(131/255,131/255,131/255,1) : Qt.rgba(160/255,160/255,160/255,1)
+ id:control
+ enabled: !disabled
+ color: {
+ if(!enabled){
+ return disableColor
+ }
+ return normalColor
+ }
+ font:FluTextStyle.Body
+ padding: 7
+ rightPadding: 40
+ leftPadding: padding+4
+ echoMode:btn_reveal.pressed ? TextField.Normal : TextField.Password
+ renderType: FluTheme.nativeText ? Text.NativeRendering : Text.QtRendering
+ selectionColor: FluTools.colorAlpha(FluTheme.primaryColor,0.5)
+ selectedTextColor: color
+ placeholderTextColor: {
+ if(!enabled){
+ return placeholderDisableColor
+ }
+ if(focus){
+ return placeholderFocusColor
+ }
+ return placeholderNormalColor
+ }
+ selectByMouse: true
+ width: 240
+ background: FluTextBoxBackground{
+ inputItem: control
+ }
+ Keys.onEnterPressed: (event)=> d.handleCommit(event)
+ Keys.onReturnPressed:(event)=> d.handleCommit(event)
+ QtObject{
+ id:d
+ function handleCommit(event){
+ control.commit(control.text)
+ }
+ }
+ FluIconButton{
+ id:btn_reveal
+ iconSource:FluentIcons.RevealPasswordMedium
+ iconSize: 10
+ width: 30
+ height: 20
+ verticalPadding: 0
+ horizontalPadding: 0
+ iconColor: FluTheme.dark ? Qt.rgba(222/255,222/255,222/255,1) : Qt.rgba(97/255,97/255,97/255,1)
+ visible: control.text !== ""
+ anchors{
+ verticalCenter: parent.verticalCenter
+ right: parent.right
+ rightMargin: 5
+ }
+ }
+ FluTextBoxMenu{
+ id:menu
+ inputItem: control
+ }
+}
diff --git a/src/FluentUI/Controls/FluPivot.qml b/src/FluentUI/Controls/FluPivot.qml
new file mode 100644
index 00000000..df0374ca
--- /dev/null
+++ b/src/FluentUI/Controls/FluPivot.qml
@@ -0,0 +1,95 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+Page {
+ default property alias content: d.children
+ property alias currentIndex: nav_list.currentIndex
+ property color textNormalColor: FluTheme.dark ? FluColors.Grey120 : FluColors.Grey120
+ property color textHoverColor: FluTheme.dark ? FluColors.Grey10 : FluColors.Black
+ property int textSize: 28
+ property bool textBold: true
+ property int textSpacing: 10
+ property int headerSpacing: 20
+ property int headerHeight: 40
+ id:control
+ width: 400
+ height: 300
+ implicitHeight: height
+ implicitWidth: width
+ FluObject{
+ id:d
+ property int tabY: control.headerHeight/2+control.textSize/2 + 3
+ }
+ background:Item{}
+ header:ListView{
+ id:nav_list
+ implicitHeight: control.headerHeight
+ implicitWidth: control.width
+ model:d.children
+ spacing: control.headerSpacing
+ interactive: false
+ orientation: ListView.Horizontal
+ highlightMoveDuration: FluTheme.enableAnimation ? 167 : 0
+ highlight: Item{
+ clip: true
+ Rectangle{
+ height: 3
+ radius: 1.5
+ color: FluTheme.primaryColor
+ width: nav_list.currentItem ? nav_list.currentItem.width : 0
+ y:d.tabY
+ Behavior on width {
+ enabled: FluTheme.enableAnimation
+ NumberAnimation{
+ duration: 167
+ easing.type: Easing.OutCubic
+ }
+ }
+ }
+ }
+ delegate: Button{
+ id:item_button
+ width: item_title.width
+ height: nav_list.height
+ focusPolicy:Qt.TabFocus
+ background:Item{
+ FluFocusRectangle{
+ anchors.margins: -4
+ visible: item_button.activeFocus
+ radius:4
+ }
+ }
+ contentItem: Item{
+ FluText {
+ id:item_title
+ text: modelData.title
+ anchors.centerIn: parent
+ font.pixelSize: control.textSize
+ font.bold: control.textBold
+ color: {
+ if(item_button.hovered || nav_list.currentIndex === index)
+ return textHoverColor
+ return textNormalColor
+ }
+ }
+ }
+ onClicked: {
+ nav_list.currentIndex = index
+ }
+ }
+ }
+ Item{
+ id:container
+ anchors.fill: parent
+ Repeater{
+ model:d.children
+ FluLoader{
+ property var argument: modelData.argument
+ anchors.fill: parent
+ sourceComponent: modelData.contentItem
+ visible: nav_list.currentIndex === index
+ }
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluPivotItem.qml b/src/FluentUI/Controls/FluPivotItem.qml
new file mode 100644
index 00000000..b241dc44
--- /dev/null
+++ b/src/FluentUI/Controls/FluPivotItem.qml
@@ -0,0 +1,9 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+QtObject {
+ property string title
+ property Component contentItem
+ property var argument
+}
diff --git a/src/FluentUI/Controls/FluPopup.qml b/src/FluentUI/Controls/FluPopup.qml
new file mode 100644
index 00000000..78d9d5bb
--- /dev/null
+++ b/src/FluentUI/Controls/FluPopup.qml
@@ -0,0 +1,54 @@
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import QtQuick.Window
+import FluentUI
+
+Popup {
+ id: control
+ padding: 0
+ modal:true
+ parent: Overlay.overlay
+ x: Math.round((d.parentWidth - width) / 2)
+ y: Math.round((d.parentHeight - height) / 2)
+ closePolicy: Popup.CloseOnEscape
+ enter: Transition {
+ NumberAnimation {
+ property: "opacity"
+ duration: FluTheme.enableAnimation ? 83 : 0
+ from:0
+ to:1
+ }
+ }
+ height:Math.min(implicitHeight,d.parentHeight)
+ exit:Transition {
+ NumberAnimation {
+ property: "opacity"
+ duration: FluTheme.enableAnimation ? 83 : 0
+ from:1
+ to:0
+ }
+ }
+ background: FluRectangle{
+ radius: [5,5,5,5]
+ color: FluTheme.dark ? Qt.rgba(43/255,43/255,43/255,1) : Qt.rgba(1,1,1,1)
+ FluShadow{
+ radius: 5
+ }
+ }
+ QtObject{
+ id:d
+ property int parentHeight: {
+ if(control.parent){
+ return control.parent.height
+ }
+ return control.height
+ }
+ property int parentWidth: {
+ if(control.parent){
+ return control.parent.width
+ }
+ return control.width
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluProgressBar.qml b/src/FluentUI/Controls/FluProgressBar.qml
new file mode 100644
index 00000000..bca37cea
--- /dev/null
+++ b/src/FluentUI/Controls/FluProgressBar.qml
@@ -0,0 +1,69 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Basic
+import FluentUI
+
+ProgressBar{
+ property int duration: 888
+ property real strokeWidth: 6
+ property bool progressVisible: false
+ property color color: FluTheme.primaryColor
+ property color backgroundColor : FluTheme.dark ? Qt.rgba(99/255,99/255,99/255,1) : Qt.rgba(214/255,214/255,214/255,1)
+ id:control
+ indeterminate : true
+ QtObject{
+ id:d
+ property real _radius: strokeWidth/2
+ }
+ onIndeterminateChanged:{
+ if(!indeterminate){
+ animator_x.duration = 0
+ rect_progress.x = 0
+ animator_x.duration = control.duration
+ }
+ }
+ background: Rectangle {
+ implicitWidth: 150
+ implicitHeight: control.strokeWidth
+ color: control.backgroundColor
+ radius: d._radius
+ }
+ contentItem: FluClip {
+ clip: true
+ radius: [d._radius,d._radius,d._radius,d._radius]
+ Rectangle {
+ id:rect_progress
+ width: {
+ if(control.indeterminate){
+ return 0.5 * parent.width
+ }
+ return control.visualPosition * parent.width
+ }
+ height: parent.height
+ radius: d._radius
+ color: control.color
+ PropertyAnimation on x {
+ id:animator_x
+ running: control.indeterminate && control.visible
+ from: -rect_progress.width
+ to:control.width+rect_progress.width
+ loops: Animation.Infinite
+ duration: control.duration
+ }
+ }
+ }
+ FluText{
+ text:(control.visualPosition * 100).toFixed(0) + "%"
+ visible: {
+ if(control.indeterminate){
+ return false
+ }
+ return control.progressVisible
+ }
+ anchors{
+ left: parent.left
+ leftMargin: control.width+5
+ verticalCenter: parent.verticalCenter
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluProgressButton.qml b/src/FluentUI/Controls/FluProgressButton.qml
new file mode 100644
index 00000000..1c21c78e
--- /dev/null
+++ b/src/FluentUI/Controls/FluProgressButton.qml
@@ -0,0 +1,136 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Basic
+import FluentUI
+
+Button {
+ property real progress
+ property bool disabled: false
+ property string contentDescription: ""
+ QtObject{
+ id:d
+ property bool checked: (rect_back.height === background.height) && (progress === 1)
+ }
+ property color normalColor: {
+ if(d.checked){
+ return FluTheme.primaryColor
+ }else{
+ return FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(254/255,254/255,254/255,1)
+ }
+ }
+ property color hoverColor: {
+ if(d.checked){
+ return FluTheme.dark ? Qt.darker(normalColor,1.1) : Qt.lighter(normalColor,1.1)
+ }else{
+ return FluTheme.dark ? Qt.rgba(68/255,68/255,68/255,1) : Qt.rgba(246/255,246/255,246/255,1)
+ }
+ }
+ property color disableColor: {
+ if(d.checked){
+ return FluTheme.dark ? Qt.rgba(82/255,82/255,82/255,1) : Qt.rgba(199/255,199/255,199/255,1)
+ }else{
+ return FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(244/255,244/255,244/255,1)
+ }
+ }
+ property color pressedColor: FluTheme.dark ? Qt.darker(normalColor,1.2) : Qt.lighter(normalColor,1.2)
+ Accessible.role: Accessible.Button
+ Accessible.name: control.text
+ Accessible.description: contentDescription
+ Accessible.onPressAction: control.clicked()
+ focusPolicy:Qt.TabFocus
+ id: control
+ enabled: !disabled
+ verticalPadding: 0
+ horizontalPadding:12
+ background: FluClip{
+ implicitWidth: 28
+ implicitHeight: 28
+ radius: [4,4,4,4]
+ Rectangle{
+ anchors.fill: parent
+ border.color: FluTheme.dark ? "#505050" : "#DFDFDF"
+ border.width: d.checked ? 0 : 1
+ radius: 4
+ color:{
+ if(!enabled){
+ return disableColor
+ }
+ if(d.checked){
+ if(pressed){
+ return pressedColor
+ }
+ }
+ return hovered ? hoverColor :normalColor
+ }
+ }
+ Rectangle{
+ id:rect_back
+ width: parent.width * control.progress
+ height: control.progress === 1 ? background.height : 3
+ visible: !d.checked
+ color: FluTheme.primaryColor
+ anchors.bottom: parent.bottom
+ Behavior on height{
+ enabled: control.progress !== 0
+ SequentialAnimation {
+ PauseAnimation {
+ duration: FluTheme.enableAnimation ? 167 : 0
+ }
+ NumberAnimation{
+ duration: FluTheme.enableAnimation ? 167 : 0
+ from: 3
+ to: background.height
+ }
+ }
+ }
+ Behavior on width{
+ NumberAnimation{
+ duration: 167
+ }
+ }
+ }
+ FluFocusRectangle{
+ visible: control.activeFocus
+ radius:4
+ }
+ }
+ contentItem: FluText {
+ text: control.text
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: {
+ if(d.checked){
+ if(FluTheme.dark){
+ if(!enabled){
+ return Qt.rgba(173/255,173/255,173/255,1)
+ }
+ return Qt.rgba(0,0,0,1)
+ }else{
+ return Qt.rgba(1,1,1,1)
+ }
+ }else{
+ if(FluTheme.dark){
+ if(!enabled){
+ return Qt.rgba(131/255,131/255,131/255,1)
+ }
+ if(!d.checked){
+ if(pressed){
+ return Qt.rgba(162/255,162/255,162/255,1)
+ }
+ }
+ return Qt.rgba(1,1,1,1)
+ }else{
+ if(!enabled){
+ return Qt.rgba(160/255,160/255,160/255,1)
+ }
+ if(!d.checked){
+ if(pressed){
+ return Qt.rgba(96/255,96/255,96/255,1)
+ }
+ }
+ return Qt.rgba(0,0,0,1)
+ }
+ }
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluProgressRing.qml b/src/FluentUI/Controls/FluProgressRing.qml
new file mode 100644
index 00000000..480aee9d
--- /dev/null
+++ b/src/FluentUI/Controls/FluProgressRing.qml
@@ -0,0 +1,93 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Basic
+import FluentUI
+
+ProgressBar{
+ property int duration: 2000
+ property real strokeWidth: 6
+ property bool progressVisible: false
+ property color color: FluTheme.primaryColor
+ property color backgroundColor : FluTheme.dark ? Qt.rgba(99/255,99/255,99/255,1) : Qt.rgba(214/255,214/255,214/255,1)
+ id:control
+ indeterminate : true
+ clip: true
+ background: Rectangle {
+ implicitWidth: 56
+ implicitHeight: 56
+ radius: control.width/2
+ color:"transparent"
+ border.color: control.backgroundColor
+ border.width: control.strokeWidth
+ }
+ onIndeterminateChanged:{
+ canvas.requestPaint()
+ }
+ QtObject{
+ id:d
+ property real _radius: control.width/2-control.strokeWidth/2
+ property real _progress: control.indeterminate ? 0.0 : control.visualPosition
+ on_ProgressChanged: {
+ canvas.requestPaint()
+ }
+ }
+ Connections{
+ target: FluTheme
+ function onDarkChanged(){
+ canvas.requestPaint()
+ }
+ }
+ contentItem: Item {
+ id:layout_item
+ Canvas {
+ id:canvas
+ anchors.fill: parent
+ antialiasing: true
+ renderTarget: Canvas.Image
+ property real startAngle: 0
+ property real sweepAngle: 0
+ SequentialAnimation on startAngle {
+ loops: Animation.Infinite
+ running: control.visible && control.indeterminate
+ PropertyAnimation { from: 0; to: 450; duration: control.duration/2 }
+ PropertyAnimation { from: 450; to: 1080; duration: control.duration/2 }
+ }
+ SequentialAnimation on sweepAngle {
+ loops: Animation.Infinite
+ running: control.visible && control.indeterminate
+ PropertyAnimation { from: 0; to: 180; duration: control.duration/2 }
+ PropertyAnimation { from: 180; to: 0; duration: control.duration/2 }
+ }
+ onStartAngleChanged: {
+ requestPaint()
+ }
+ onPaint: {
+ var ctx = canvas.getContext("2d")
+ ctx.clearRect(0, 0, canvas.width, canvas.height)
+ ctx.save()
+ ctx.lineWidth = control.strokeWidth
+ ctx.strokeStyle = control.color
+ ctx.lineCap = "round"
+ ctx.beginPath()
+ if(control.indeterminate){
+ ctx.arc(width/2, height/2, d._radius , Math.PI * (startAngle - 90) / 180, Math.PI * (startAngle - 90 + sweepAngle) / 180)
+ }else{
+ ctx.arc(width/2, height/2, d._radius , -0.5 * Math.PI , -0.5 * Math.PI + d._progress * 2 * Math.PI)
+ }
+ ctx.stroke()
+ ctx.closePath()
+ ctx.restore()
+ }
+ }
+ }
+ FluText{
+ text:(control.visualPosition * 100).toFixed(0) + "%"
+ visible: {
+ if(control.indeterminate){
+ return false
+ }
+ return control.progressVisible
+ }
+ anchors.centerIn: parent
+ }
+}
diff --git a/src/FluentUI/Controls/FluQRCode.qml b/src/FluentUI/Controls/FluQRCode.qml
new file mode 100644
index 00000000..9a6428b4
--- /dev/null
+++ b/src/FluentUI/Controls/FluQRCode.qml
@@ -0,0 +1,23 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+Item{
+ property alias text: qrcode.text
+ property alias color: qrcode.color
+ property alias bgColor: qrcode.bgColor
+ property int size: 50
+ property int margins: 0
+ id:control
+ width: size
+ height: size
+ Rectangle{
+ color: bgColor
+ anchors.fill: parent
+ }
+ FluQrCodeItem{
+ id:qrcode
+ size:control.size-margins
+ anchors.centerIn: parent
+ }
+}
diff --git a/src/FluentUI/Controls/FluRadioButton.qml b/src/FluentUI/Controls/FluRadioButton.qml
new file mode 100644
index 00000000..ac67b5ee
--- /dev/null
+++ b/src/FluentUI/Controls/FluRadioButton.qml
@@ -0,0 +1,94 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Basic
+import QtQuick.Layouts
+import FluentUI
+
+Button {
+ property string contentDescription: ""
+ property bool disabled: false
+ property color borderNormalColor: checked ? FluTheme.primaryColor : FluTheme.dark ? Qt.rgba(161/255,161/255,161/255,1) : Qt.rgba(141/255,141/255,141/255,1)
+ property color borderDisableColor: FluTheme.dark ? Qt.rgba(82/255,82/255,82/255,1) : Qt.rgba(198/255,198/255,198/255,1)
+ property color normalColor: FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(1,1,1,1)
+ property color hoverColor: checked ? FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(1,1,1,1) : FluTheme.dark ? Qt.rgba(43/255,43/255,43/255,1) : Qt.rgba(222/255,222/255,222/255,1)
+ property color disableColor: checked ? FluTheme.dark ? Qt.rgba(159/255,159/255,159/255,1) : Qt.rgba(159/255,159/255,159/255,1) : FluTheme.dark ? Qt.rgba(43/255,43/255,43/255,1) : Qt.rgba(222/255,222/255,222/255,1)
+ property alias textColor: btn_text.textColor
+ property real size: 18
+ property bool textRight: true
+ property real textSpacing: 6
+ property var clickListener : function(){
+ checked = !checked
+ }
+ Accessible.role: Accessible.Button
+ Accessible.name: control.text
+ Accessible.description: contentDescription
+ Accessible.onPressAction: control.clicked()
+ id:control
+ enabled: !disabled
+ horizontalPadding:2
+ verticalPadding: 2
+ background: Item{
+ FluFocusRectangle{
+ visible: control.activeFocus
+ }
+ }
+ focusPolicy:Qt.TabFocus
+ font:FluTextStyle.Body
+ onClicked: clickListener()
+ contentItem: RowLayout{
+ spacing: control.textSpacing
+ layoutDirection:control.textRight ? Qt.LeftToRight : Qt.RightToLeft
+ Rectangle{
+ id:rect_check
+ width: control.size
+ height: control.size
+ radius: size/2
+ border.width: {
+ if(checked&&!enabled){
+ return 3
+ }
+ if(pressed){
+ if(checked){
+ return 4
+ }
+ return 1
+ }
+ if(hovered){
+ if(checked){
+ return 3
+ }
+ return 1
+ }
+ return checked ? 4 : 1
+ }
+ Behavior on border.width {
+ enabled: FluTheme.enableAnimation
+ NumberAnimation{
+ duration: 167
+ easing.type: Easing.OutCubic
+ }
+ }
+ border.color: {
+ if(!enabled){
+ return borderDisableColor
+ }
+ return borderNormalColor
+ }
+ color:{
+ if(!enabled){
+ return disableColor
+ }
+ if(hovered){
+ return hoverColor
+ }
+ return normalColor
+ }
+ }
+ FluText{
+ id:btn_text
+ text: control.text
+ Layout.alignment: Qt.AlignVCenter
+ font: control.font
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluRadioButtons.qml b/src/FluentUI/Controls/FluRadioButtons.qml
new file mode 100644
index 00000000..eb8c817d
--- /dev/null
+++ b/src/FluentUI/Controls/FluRadioButtons.qml
@@ -0,0 +1,33 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Basic
+import QtQuick.Layouts
+import FluentUI
+
+ColumnLayout {
+ default property alias buttons: control.data
+ property int currentIndex : -1
+ id:control
+ onCurrentIndexChanged: {
+ for(var i = 0;i{
+ d.mouseValue = Number(mouse.x / d.itemSize)+1
+ }
+ onExited: {
+ d.mouseValue = 0
+ }
+ onClicked: (mouse)=>{
+ control.value = Number(mouse.x / d.itemSize)+1
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluRemoteLoader.qml b/src/FluentUI/Controls/FluRemoteLoader.qml
new file mode 100644
index 00000000..13c7142d
--- /dev/null
+++ b/src/FluentUI/Controls/FluRemoteLoader.qml
@@ -0,0 +1,39 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+FluStatusLayout {
+ property url source: ""
+ property bool lazy: false
+ color:"transparent"
+ id:control
+ onErrorClicked: {
+ reload()
+ }
+ Component.onCompleted: {
+ if(!lazy){
+ loader.source = control.source
+ }
+ }
+ FluLoader{
+ id:loader
+ anchors.fill: parent
+ asynchronous: true
+ onStatusChanged: {
+ if(status === Loader.Error){
+ control.statusMode = FluStatusLayoutType.Error
+ }else if(status === Loader.Loading){
+ control.statusMode = FluStatusLayoutType.Loading
+ }else{
+ control.statusMode = FluStatusLayoutType.Success
+ }
+ }
+ }
+ function reload(){
+ var timestamp = Date.now();
+ loader.source = control.source+"?"+timestamp
+ }
+ function itemLodaer(){
+ return loader
+ }
+}
diff --git a/src/FluentUI/Controls/FluScrollBar.qml b/src/FluentUI/Controls/FluScrollBar.qml
new file mode 100644
index 00000000..0d66e6a8
--- /dev/null
+++ b/src/FluentUI/Controls/FluScrollBar.qml
@@ -0,0 +1,187 @@
+import QtQuick
+import QtQuick.Controls.impl
+import QtQuick.Templates as T
+import FluentUI
+
+T.ScrollBar {
+ id: control
+
+ property color color : FluTheme.dark ? Qt.rgba(159/255,159/255,159/255,1) : Qt.rgba(138/255,138/255,138/255,1)
+ property color pressedColor: FluTheme.dark ? Qt.darker(color,1.2) : Qt.lighter(color,1.2)
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+
+ visible: control.policy !== T.ScrollBar.AlwaysOff
+ minimumSize: Math.max(orientation === Qt.Horizontal ? height / width : width / height,0.3)
+ QtObject{
+ id:d
+ property int minLine : 2
+ property int maxLine : 6
+ }
+ z: horizontal? 10 : 20
+ verticalPadding : vertical ? 15 : 3
+ horizontalPadding : horizontal ? 15 : 3
+ background: Rectangle{
+ id:back_rect
+ radius: 5
+ color:FluTheme.dark ? Qt.rgba(44/255,44/255,44/255,1) : Qt.rgba(255/255,255/255,255/255,1)
+ opacity:{
+ if(vertical){
+ return d.maxLine === Number(rect_bar.width)
+ }
+ return d.maxLine === Number(rect_bar.height)
+ }
+ Behavior on opacity {
+ NumberAnimation{
+ duration: 50
+ }
+ }
+ }
+ FluIconButton{
+ width: 12
+ height: 12
+ iconSize: 8
+ verticalPadding: 0
+ horizontalPadding: 0
+ visible: control.horizontal
+ opacity: back_rect.opacity
+ anchors{
+ left: parent.left
+ leftMargin: 2
+ verticalCenter: parent.verticalCenter
+ }
+ iconColor: control.color
+ iconSource: FluentIcons.CaretLeftSolid8
+ onClicked: {
+ control.decrease()
+ }
+ }
+ FluIconButton{
+ width: 12
+ height: 12
+ iconSize: 8
+ verticalPadding: 0
+ horizontalPadding: 0
+ iconColor: control.color
+ opacity: back_rect.opacity
+ anchors{
+ right: parent.right
+ rightMargin: 2
+ verticalCenter: parent.verticalCenter
+ }
+ visible: control.horizontal
+ iconSource: FluentIcons.CaretRightSolid8
+ onClicked: {
+ control.increase()
+ }
+ }
+ FluIconButton{
+ width: 12
+ height: 12
+ iconSize: 8
+ verticalPadding: 0
+ horizontalPadding: 0
+ iconColor: control.color
+ opacity: back_rect.opacity
+ anchors{
+ top: parent.top
+ topMargin: 2
+ horizontalCenter: parent.horizontalCenter
+ }
+ visible: control.vertical
+ iconSource: FluentIcons.CaretUpSolid8
+ onClicked: {
+ control.decrease()
+ }
+ }
+ FluIconButton{
+ width: 12
+ height: 12
+ iconSize: 8
+ verticalPadding: 0
+ horizontalPadding: 0
+ iconColor: control.color
+ opacity: back_rect.opacity
+ anchors{
+ bottom: parent.bottom
+ bottomMargin: 2
+ horizontalCenter: parent.horizontalCenter
+ }
+ visible: control.vertical
+ iconSource: FluentIcons.CaretDownSolid8
+ onClicked: {
+ control.increase()
+ }
+ }
+ contentItem: Item {
+ property bool collapsed: (control.policy === T.ScrollBar.AlwaysOn || (control.active && control.size < 1.0))
+ implicitWidth: control.interactive ? d.maxLine : d.minLine
+ implicitHeight: control.interactive ? d.maxLine : d.minLine
+ Rectangle{
+ id:rect_bar
+ width: vertical ? d.minLine : parent.width
+ height: horizontal ? d.minLine : parent.height
+ color:{
+ if(control.pressed){
+ return control.pressedColor
+ }
+ return control .color
+ }
+ anchors{
+ right: vertical ? parent.right : undefined
+ bottom: horizontal ? parent.bottom : undefined
+ }
+ radius: width / 2
+ visible: control.size < 1.0
+ }
+ states: [
+ State{
+ name:"show"
+ when: contentItem.collapsed
+ PropertyChanges {
+ target: rect_bar
+ width: vertical ? d.maxLine : parent.width
+ height: horizontal ? d.maxLine : parent.height
+ }
+ }
+ ,State{
+ name:"hide"
+ when: !contentItem.collapsed
+ PropertyChanges {
+ target: rect_bar
+ width: vertical ? d.minLine : parent.width
+ height: horizontal ? d.minLine : parent.height
+ }
+ }
+ ]
+ transitions:[
+ Transition {
+ to: "hide"
+ SequentialAnimation {
+ PauseAnimation { duration: 450 }
+ NumberAnimation {
+ target: rect_bar
+ properties: vertical ? "width" : "height"
+ duration: 167
+ easing.type: Easing.OutCubic
+ }
+ }
+ }
+ ,Transition {
+ to: "show"
+ SequentialAnimation {
+ PauseAnimation { duration: 450 }
+ NumberAnimation {
+ target: rect_bar
+ properties: vertical ? "width" : "height"
+ duration: 167
+ easing.type: Easing.OutCubic
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/src/FluentUI/Controls/FluScrollIndicator.qml b/src/FluentUI/Controls/FluScrollIndicator.qml
new file mode 100644
index 00000000..3e4b1685
--- /dev/null
+++ b/src/FluentUI/Controls/FluScrollIndicator.qml
@@ -0,0 +1,49 @@
+import QtQuick
+import QtQuick.Controls.impl
+import QtQuick.Templates as T
+
+T.ScrollIndicator {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+
+ padding: 2
+
+ contentItem: Rectangle {
+ implicitWidth: 2
+ implicitHeight: 2
+
+ color: control.palette.mid
+ visible: control.size < 1.0
+ opacity: 0.0
+
+ states: State {
+ name: "active"
+ when: control.active
+ PropertyChanges {
+ target: control
+ contentItem.opacity: 0.75
+ }
+ }
+
+ transitions: [
+ Transition {
+ from: "active"
+ SequentialAnimation {
+ PauseAnimation {
+ duration: 450
+ }
+ NumberAnimation {
+ target: control.contentItem
+ duration: 200
+ property: "opacity"
+ to: 0.0
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/src/FluentUI/Controls/FluScrollablePage.qml b/src/FluentUI/Controls/FluScrollablePage.qml
new file mode 100644
index 00000000..6aafc0c9
--- /dev/null
+++ b/src/FluentUI/Controls/FluScrollablePage.qml
@@ -0,0 +1,79 @@
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Window
+import QtQuick.Controls
+import FluentUI
+
+FluPage {
+ property alias title: text_title.text
+ default property alias content: container.data
+ property int spacing : 0
+ property int leftPadding: 10
+ property int topPadding: 0
+ property int rightPadding: 10
+ property int bottomPadding: 10
+ property alias color: status_view.color
+ property alias statusMode: status_view.statusMode
+ property alias loadingText: status_view.loadingText
+ property alias emptyText:status_view.emptyText
+ property alias errorText:status_view.errorText
+ property alias errorButtonText:status_view.errorButtonText
+ property alias loadingItem :status_view.loadingItem
+ property alias emptyItem : status_view.emptyItem
+ property alias errorItem :status_view.errorItem
+ signal errorClicked
+ id:control
+ FluText{
+ id:text_title
+ font: FluTextStyle.Title
+ visible: text !== ""
+ height: visible ? contentHeight : 0
+ padding: 0
+ anchors{
+ top: parent.top
+ topMargin: control.topPadding
+ left: parent.left
+ right: parent.right
+ leftMargin: control.leftPadding
+ rightMargin: control.rightPadding
+ }
+ }
+ FluStatusLayout{
+ id:status_view
+ color: "#00000000"
+ statusMode: FluStatusLayoutType.Success
+ onErrorClicked: control.errorClicked()
+ anchors{
+ left: parent.left
+ right: parent.right
+ top: text_title.bottom
+ bottom: parent.bottom
+ bottomMargin: control.bottomPadding
+ }
+ Flickable{
+ id:flickview
+ clip: true
+ anchors.fill: parent
+ contentWidth: parent.width
+ contentHeight: container.height
+ ScrollBar.vertical: FluScrollBar {
+ anchors.right: flickview.right
+ anchors.rightMargin: 2
+ }
+ boundsBehavior: Flickable.StopAtBounds
+ ColumnLayout{
+ id:container
+ spacing: control.spacing
+ clip: true
+ anchors{
+ left: parent.left
+ right: parent.right
+ top: parent.top
+ leftMargin: control.leftPadding
+ rightMargin: control.rightPadding
+ }
+ width: parent.width
+ }
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluShadow.qml b/src/FluentUI/Controls/FluShadow.qml
new file mode 100644
index 00000000..af236e1e
--- /dev/null
+++ b/src/FluentUI/Controls/FluShadow.qml
@@ -0,0 +1,24 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+Item {
+ //高性能阴影!!!比DropShadow阴影性能高出数倍!!!
+ property color color: FluTheme.dark ? "#FFFFFF" : "#999999"
+ property int elevation: 6
+ property int radius: 4
+ id:control
+ anchors.fill: parent
+ Repeater{
+ model: elevation
+ Rectangle{
+ anchors.fill: parent
+ color: "#00000000"
+ opacity: 0.01 * (elevation-index+1)
+ anchors.margins: -index
+ radius: control.radius+index
+ border.width: index
+ border.color: control.color
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluShortcutPicker.qml b/src/FluentUI/Controls/FluShortcutPicker.qml
new file mode 100644
index 00000000..f3a592b8
--- /dev/null
+++ b/src/FluentUI/Controls/FluShortcutPicker.qml
@@ -0,0 +1,235 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+FluIconButton {
+ id:control
+ property var current : ["Ctrl","Shift","A"]
+ property string title: qsTr("Activate the Shortcut")
+ property string message: qsTr("Press the key combination to change the shortcut")
+ property string positiveText: qsTr("Save")
+ property string neutralText: qsTr("Cancel")
+ property string negativeText: qsTr("Reset")
+ signal accepted()
+ QtObject{
+ id: d
+ function keyToString(key_code,shift = true)
+ {
+ switch(key_code)
+ {
+ case Qt.Key_Period: return ".";
+ case Qt.Key_Greater: return shift ? ">" : ".";
+ case Qt.Key_Comma: return ",";
+ case Qt.Key_Less: return shift ? "<" : ",";
+ case Qt.Key_Slash: return "/";
+ case Qt.Key_Question: return shift ? "?" : "/";
+ case Qt.Key_Semicolon: return ";";
+ case Qt.Key_Colon: return shift ? ":" : ";";
+ case Qt.Key_Apostrophe: return "'";
+ case Qt.Key_QuoteDbl: return shift ? "'" : "\"";
+ case Qt.Key_QuoteLeft: return "`";
+ case Qt.Key_AsciiTilde: return shift ? "~" : "`";
+ case Qt.Key_Minus: return "-";
+ case Qt.Key_Underscore: return shift ? "_" : "-";
+ case Qt.Key_Equal: return "=";
+ case Qt.Key_Plus: return shift ? "+" : "=";
+ case Qt.Key_BracketLeft: return "[";
+ case Qt.Key_BraceLeft: return shift ? "{" : "[";
+ case Qt.Key_BracketRight: return "]";
+ case Qt.Key_BraceRight: return shift ? "}" : "]";
+ case Qt.Key_Backslash: return "\\";
+ case Qt.Key_Bar: return shift ? "|" : "\\";
+ case Qt.Key_Up: return "Up";
+ case Qt.Key_Down: return "Down";
+ case Qt.Key_Right: return "Right";
+ case Qt.Key_Left: return "Left";
+ case Qt.Key_Space: return "Space";
+ case Qt.Key_PageDown: return "PgDown";
+ case Qt.Key_PageUp: return "PgUp";
+ case Qt.Key_0: return "0";
+ case Qt.Key_1: return "1";
+ case Qt.Key_2: return "2";
+ case Qt.Key_3: return "3";
+ case Qt.Key_4: return "4";
+ case Qt.Key_5: return "5";
+ case Qt.Key_6: return "6";
+ case Qt.Key_7: return "7";
+ case Qt.Key_8: return "8";
+ case Qt.Key_9: return "9";
+ case Qt.Key_Exclam: return shift ? "!" : "1";
+ case Qt.Key_At: return shift ? "@" : "2";
+ case Qt.Key_NumberSign: return shift ? "#" : "3";
+ case Qt.Key_Dollar: return shift ? "$" : "4";
+ case Qt.Key_Percent: return shift ? "%" : "5";
+ case Qt.Key_AsciiCircum: return shift ? "^" : "6";
+ case Qt.Key_Ampersand: return shift ? "&" : "7";
+ case Qt.Key_Asterisk: return shift ? "*" : "8";
+ case Qt.Key_ParenLeft: return shift ? "(" : "9";
+ case Qt.Key_ParenRight: return shift ? ")" : "0";
+ case Qt.Key_A: return "A";
+ case Qt.Key_B: return "B";
+ case Qt.Key_C: return "C";
+ case Qt.Key_D: return "D";
+ case Qt.Key_E: return "E";
+ case Qt.Key_F: return "F";
+ case Qt.Key_G: return "G";
+ case Qt.Key_H: return "H";
+ case Qt.Key_I: return "I";
+ case Qt.Key_J: return "J";
+ case Qt.Key_K: return "K";
+ case Qt.Key_L: return "L";
+ case Qt.Key_M: return "M";
+ case Qt.Key_N: return "N";
+ case Qt.Key_O: return "O";
+ case Qt.Key_P: return "P";
+ case Qt.Key_Q: return "Q";
+ case Qt.Key_R: return "R";
+ case Qt.Key_S: return "S";
+ case Qt.Key_T: return "T";
+ case Qt.Key_U: return "U";
+ case Qt.Key_V: return "V";
+ case Qt.Key_W: return "W";
+ case Qt.Key_X: return "X";
+ case Qt.Key_Y: return "Y";
+ case Qt.Key_Z: return "Z";
+ case Qt.Key_F1: return "F1";
+ case Qt.Key_F2: return "F2";
+ case Qt.Key_F3: return "F3";
+ case Qt.Key_F4: return "F4";
+ case Qt.Key_F5: return "F5";
+ case Qt.Key_F6: return "F6";
+ case Qt.Key_F7: return "F7";
+ case Qt.Key_F8: return "F8";
+ case Qt.Key_F9: return "F9";
+ case Qt.Key_F10: return "F10";
+ case Qt.Key_F11: return "F11";
+ case Qt.Key_F12: return "F12";
+ case Qt.Key_Home: return "Home";
+ case Qt.Key_End: return "End";
+ case Qt.Key_Insert: return "Insert";
+ case Qt.Key_Delete: return "Delete";
+ }
+ return "";
+ }
+ }
+ background: Rectangle{
+ border.color: FluTheme.dark ? "#505050" : "#DFDFDF"
+ border.width: 1
+ implicitHeight: 42
+ implicitWidth: layout_row.width+28
+ radius: control.radius
+ color:control.color
+ FluFocusRectangle{
+ visible: control.activeFocus
+ }
+ }
+ Component{
+ id:com_item_key
+ Rectangle{
+ id:item_key_control
+ color:FluTheme.primaryColor
+ width: Math.max(item_text.implicitWidth+12,28)
+ height: Math.max(item_text.implicitHeight,28)
+ radius: 4
+ Text{
+ id:item_text
+ color: FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
+ font.pixelSize: 13
+ text: keyText
+ anchors.centerIn: parent
+ }
+ }
+ }
+ Row{
+ id:layout_row
+ spacing: 5
+ anchors.centerIn: parent
+ Repeater{
+ model: control.current
+ delegate: Loader{
+ property var keyText: modelData
+ sourceComponent: com_item_key
+ }
+ }
+ Item{
+ width: 3
+ height: 1
+ }
+ FluIcon{
+ iconSource: FluentIcons.EditMirrored
+ iconSize: 13
+ anchors{
+ verticalCenter: parent.verticalCenter
+ }
+ }
+ }
+ FluContentDialog{
+ id:content_dialog
+ property var keysModel: []
+ title: control.title
+ message: control.message
+ buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton | FluContentDialogType.NeutralButton
+ positiveText: control.positiveText
+ neutralText: control.neutralText
+ negativeText: control.negativeText
+ onVisibleChanged: {
+ if(visible){
+ content_dialog.keysModel = control.current
+ }
+ }
+ onPositiveClicked: {
+ control.current = content_dialog.keysModel
+ control.accepted()
+ }
+ onNegativeClickListener: function(){
+ content_dialog.keysModel = control.current
+ }
+ contentDelegate: Component{
+ Item{
+ implicitWidth: parent.width
+ implicitHeight: 100
+ Component.onCompleted: {
+ forceActiveFocus()
+ }
+ Keys.enabled: true
+ Keys.onPressed:
+ (event)=>{
+ var keyNames = []
+ if (event.modifiers & Qt.AltModifier) {
+ keyNames.push("Alt")
+ }
+ if (event.modifiers & Qt.ControlModifier) {
+ keyNames.push("Ctrl")
+ }
+ if (event.modifiers & Qt.ShiftModifier) {
+ keyNames.push("Shift")
+ }
+ var keyName = d.keyToString(event.key,false)
+ if(keyName!==""){
+ keyNames.push(keyName)
+ content_dialog.keysModel = keyNames
+ }
+ event.accepted = true
+ }
+ Keys.onTabPressed:
+ (event)=>{
+ event.accepted = true
+ }
+ Row{
+ spacing: 5
+ anchors.centerIn: parent
+ Repeater{
+ model: content_dialog.keysModel
+ delegate: Loader{
+ property var keyText: modelData
+ sourceComponent: com_item_key
+ }
+ }
+ }
+ }
+ }
+ }
+ onClicked: {
+ content_dialog.open()
+ }
+}
diff --git a/src/FluentUI/Controls/FluSlider.qml b/src/FluentUI/Controls/FluSlider.qml
new file mode 100644
index 00000000..7b0be429
--- /dev/null
+++ b/src/FluentUI/Controls/FluSlider.qml
@@ -0,0 +1,75 @@
+import QtQuick
+import QtQuick.Controls.impl
+import QtQuick.Templates as T
+import FluentUI
+
+T.Slider {
+ property bool tooltipEnabled: true
+ property string text: String(control.value)
+ id: control
+ to:100
+ stepSize:1
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitHandleWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitHandleHeight + topPadding + bottomPadding)
+ padding: 6
+ handle: Rectangle {
+ x: control.leftPadding + (control.horizontal ? control.visualPosition * (control.availableWidth - width) : (control.availableWidth - width) / 2)
+ y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : control.visualPosition * (control.availableHeight - height))
+ implicitWidth: 20
+ implicitHeight: 20
+ radius: 10
+ color:FluTheme.dark ? Qt.rgba(69/255,69/255,69/255,1) :Qt.rgba(1,1,1,1)
+ FluShadow{
+ radius: 10
+ }
+ FluIcon{
+ width: 10
+ height: 10
+ Behavior on scale{
+ NumberAnimation{
+ duration: 167
+ easing.type: Easing.OutCubic
+ }
+ }
+ iconSource: FluentIcons.FullCircleMask
+ iconSize: 10
+ scale:{
+ if(control.pressed){
+ return 0.9
+ }
+ return control.hovered ? 1.2 : 1
+ }
+ iconColor: FluTheme.primaryColor
+ anchors.centerIn: parent
+ }
+ }
+ background: Item {
+ x: control.leftPadding + (control.horizontal ? 0 : (control.availableWidth - width) / 2)
+ y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : 0)
+ implicitWidth: control.horizontal ? 180 : 6
+ implicitHeight: control.horizontal ? 6 : 180
+ width: control.horizontal ? control.availableWidth : implicitWidth
+ height: control.horizontal ? implicitHeight : control.availableHeight
+ Rectangle{
+ anchors.fill: parent
+ anchors.margins: 1
+ radius: 2
+ color:FluTheme.dark ? Qt.rgba(162/255,162/255,162/255,1) : Qt.rgba(138/255,138/255,138/255,1)
+ }
+ scale: control.horizontal && control.mirrored ? -1 : 1
+ Rectangle {
+ y: control.horizontal ? 0 : control.visualPosition * parent.height
+ width: control.horizontal ? control.position * parent.width : 6
+ height: control.horizontal ? 6 : control.position * parent.height
+ radius: 3
+ color: FluTheme.primaryColor
+ }
+ }
+ FluTooltip{
+ parent: control.handle
+ visible: control.tooltipEnabled && (control.pressed || control.hovered)
+ text:control.text
+ }
+}
diff --git a/src/FluentUI/Controls/FluSpinBox.qml b/src/FluentUI/Controls/FluSpinBox.qml
new file mode 100644
index 00000000..c3265efa
--- /dev/null
+++ b/src/FluentUI/Controls/FluSpinBox.qml
@@ -0,0 +1,160 @@
+import QtQuick
+import QtQuick.Controls.impl
+import QtQuick.Templates as T
+import FluentUI
+
+T.SpinBox {
+ id: control
+ property bool disabled: false
+ property color normalColor: FluTheme.dark ? Qt.rgba(56/255,56/255,56/255,1) : Qt.rgba(232/255,232/255,232/255,1)
+ property color hoverColor: FluTheme.dark ? Qt.rgba(64/255,64/255,64/255,1) : Qt.rgba(224/255,224/255,224/255,1)
+ property color pressedColor: FluTheme.dark ? Qt.rgba(72/255,72/255,72/255,1) : Qt.rgba(216/255,216/255,216/255,1)
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ contentItem.implicitWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding,
+ up.implicitIndicatorHeight, down.implicitIndicatorHeight)
+ leftPadding: padding + (control.mirrored ? (up.indicator ? up.indicator.width : 0) : (down.indicator ? down.indicator.width : 0))
+ rightPadding: padding + (control.mirrored ? (down.indicator ? down.indicator.width : 0) : (up.indicator ? up.indicator.width : 0))
+ enabled: !disabled
+ validator: IntValidator {
+ locale: control.locale.name
+ bottom: Math.min(control.from, control.to)
+ top: Math.max(control.from, control.to)
+ }
+
+ contentItem: TextInput {
+ property color normalColor: FluTheme.dark ? Qt.rgba(255/255,255/255,255/255,1) : Qt.rgba(27/255,27/255,27/255,1)
+ property color disableColor: FluTheme.dark ? Qt.rgba(131/255,131/255,131/255,1) : Qt.rgba(160/255,160/255,160/255,1)
+ property color placeholderNormalColor: FluTheme.dark ? Qt.rgba(210/255,210/255,210/255,1) : Qt.rgba(96/255,96/255,96/255,1)
+ property color placeholderFocusColor: FluTheme.dark ? Qt.rgba(152/255,152/255,152/255,1) : Qt.rgba(141/255,141/255,141/255,1)
+ property color placeholderDisableColor: FluTheme.dark ? Qt.rgba(131/255,131/255,131/255,1) : Qt.rgba(160/255,160/255,160/255,1)
+ z: 2
+ text: control.displayText
+ clip: width < implicitWidth
+ padding: 6
+ font: control.font
+ color: {
+ if(!enabled){
+ return disableColor
+ }
+ return normalColor
+ }
+ selectionColor: FluTools.colorAlpha(FluTheme.primaryColor,0.5)
+ selectedTextColor: color
+ horizontalAlignment: Qt.AlignHCenter
+ verticalAlignment: Qt.AlignVCenter
+ readOnly: !control.editable
+ validator: control.validator
+ inputMethodHints: control.inputMethodHints
+ Rectangle{
+ width: parent.width
+ height: contentItem.activeFocus ? 2 : 1
+ anchors.bottom: parent.bottom
+ visible: contentItem.enabled
+ color: {
+ if(contentItem.activeFocus){
+ return FluTheme.primaryColor
+ }
+ if(FluTheme.dark){
+ return Qt.rgba(166/255,166/255,166/255,1)
+ }else{
+ return Qt.rgba(183/255,183/255,183/255,1)
+ }
+ }
+ Behavior on height{
+ enabled: FluTheme.enableAnimation
+ NumberAnimation{
+ duration: 83
+ easing.type: Easing.OutCubic
+ }
+ }
+ }
+ }
+
+ up.indicator: FluClip {
+ x: control.mirrored ? 0 : control.width - width
+ height: control.height
+ implicitWidth: 32
+ implicitHeight: 32
+ radius: [0,4,4,0]
+ Rectangle{
+ anchors.fill: parent
+ color: {
+ if(control.up.pressed){
+ return control.pressedColor
+ }
+ if(control.up.hovered){
+ return control.hoverColor
+ }
+ return control.normalColor
+ }
+ }
+ Rectangle {
+ x: (parent.width - width) / 2
+ y: (parent.height - height) / 2
+ width: parent.width / 3
+ height: 2
+ color: enabled ? FluTheme.dark ? Qt.rgba(1,1,1,1) : Qt.rgba(0,0,0,1) : FluColors.Grey90
+ }
+ Rectangle {
+ x: (parent.width - width) / 2
+ y: (parent.height - height) / 2
+ width: 2
+ height: parent.width / 3
+ color: enabled ? FluTheme.dark ? Qt.rgba(1,1,1,1) : Qt.rgba(0,0,0,1) : FluColors.Grey90
+ }
+ }
+
+
+ down.indicator: FluClip {
+ x: control.mirrored ? parent.width - width : 0
+ height: control.height
+ implicitWidth: 32
+ implicitHeight: 32
+ radius: [4,0,0,4]
+ Rectangle{
+ anchors.fill: parent
+ color: {
+ if(control.down.pressed){
+ return control.pressedColor
+ }
+ if(control.down.hovered){
+ return control.hoverColor
+ }
+ return normalColor
+ }
+ }
+ Rectangle {
+ x: (parent.width - width) / 2
+ y: (parent.height - height) / 2
+ width: parent.width / 3
+ height: 2
+ color: enabled ? FluTheme.dark ? Qt.rgba(1,1,1,1) : Qt.rgba(0,0,0,1) : FluColors.Grey90
+ }
+ }
+
+ background: Rectangle {
+ implicitWidth: 136
+ radius: 4
+ border.width: 1
+ border.color: {
+ if(contentItem.disabled){
+ return FluTheme.dark ? Qt.rgba(73/255,73/255,73/255,1) : Qt.rgba(237/255,237/255,237/255,1)
+ }
+ return FluTheme.dark ? Qt.rgba(76/255,76/255,76/255,1) : Qt.rgba(240/255,240/255,240/255,1)
+ }
+ color: {
+ if(contentItem.disabled){
+ return FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(252/255,252/255,252/255,1)
+ }
+ if(contentItem.activeFocus){
+ return FluTheme.dark ? Qt.rgba(36/255,36/255,36/255,1) : Qt.rgba(1,1,1,1)
+ }
+ if(contentItem.hovered){
+ return FluTheme.dark ? Qt.rgba(68/255,68/255,68/255,1) : Qt.rgba(251/255,251/255,251/255,1)
+ }
+ return FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(1,1,1,1)
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluSplitLayout.qml b/src/FluentUI/Controls/FluSplitLayout.qml
new file mode 100644
index 00000000..f13673a8
--- /dev/null
+++ b/src/FluentUI/Controls/FluSplitLayout.qml
@@ -0,0 +1,30 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+SplitView {
+ property color handleColor : FluTheme.dark ? Qt.rgba(159/255,159/255,159/255,1) : Qt.rgba(138/255,138/255,138/255,1)
+ id:control
+ QtObject{
+ id:d
+ property bool isVertical: control.orientation === Qt.Vertical
+ }
+ handle: Rectangle {
+ implicitWidth: d.isVertical ? control.width : 12
+ implicitHeight: d.isVertical ? 12 : control.height
+ clip: true
+ color: {
+ if(SplitHandle.pressed){
+ return FluTheme.itemPressColor
+ }
+ return SplitHandle.hovered ? FluTheme.itemHoverColor : FluTheme.itemNormalColor
+ }
+ Rectangle{
+ width: d.isVertical ? 26 : 4
+ height: d.isVertical ? 4 : 26
+ anchors.centerIn: parent
+ color: control.handleColor
+ radius: 2
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluStaggeredLayout.qml b/src/FluentUI/Controls/FluStaggeredLayout.qml
new file mode 100644
index 00000000..9f0da327
--- /dev/null
+++ b/src/FluentUI/Controls/FluStaggeredLayout.qml
@@ -0,0 +1,68 @@
+import QtQuick
+
+Item {
+ property int itemWidth : 200
+ property alias model: rep.model
+ property alias delegate: rep.delegate
+ property int rowSpacing: 8
+ property int colSpacing: 8
+ id: control
+ QtObject{
+ id:d
+ property int cellWidth : itemWidth+rowSpacing
+ property int colCount: {
+ var cols = parseInt(control.width/cellWidth)
+ return cols>0?cols:1
+ }
+ property var colsHeightArr: []
+ property int maxHeight: 0
+ property var itemsInRep: []
+ onMaxHeightChanged: {
+ control.implicitHeight = maxHeight
+ }
+ onColCountChanged: {
+ refresh()
+ }
+ function refresh(){
+ d.colsHeightArr = []
+ var count = itemsInRep.length
+ for(var i=0; i {
+ d.addToFall(index, item)
+ d.itemsInRep.push(item)
+ }
+ }
+ function clear(){
+ d.maxHeight = 0
+ d.colsHeightArr = []
+ d.itemsInRep = []
+ model.clear()
+ }
+}
diff --git a/src/FluentUI/Controls/FluStatusLayout.qml b/src/FluentUI/Controls/FluStatusLayout.qml
new file mode 100644
index 00000000..ab82ba21
--- /dev/null
+++ b/src/FluentUI/Controls/FluStatusLayout.qml
@@ -0,0 +1,116 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import FluentUI
+
+Item{
+ id:control
+ default property alias content: container.data
+ property int statusMode: FluStatusLayoutType.Loading
+ property string loadingText:"正在加载..."
+ property string emptyText: "空空如也"
+ property string errorText: "页面出错了.."
+ property string errorButtonText: "重新加载"
+ property color color: FluTheme.dark ? Window.active ? Qt.rgba(38/255,44/255,54/255,1) : Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1)
+ signal errorClicked
+ property Component loadingItem : com_loading
+ property Component emptyItem : com_empty
+ property Component errorItem : com_error
+
+ Item{
+ id:container
+ anchors.fill: parent
+ visible: statusMode===FluStatusLayoutType.Success
+ }
+ FluLoader{
+ id:loader
+ anchors.fill: parent
+ visible: statusMode!==FluStatusLayoutType.Success
+ sourceComponent: {
+ if(statusMode === FluStatusLayoutType.Loading){
+ return loadingItem
+ }
+ if(statusMode === FluStatusLayoutType.Empty){
+ return emptyItem
+ }
+ if(statusMode === FluStatusLayoutType.Error){
+ return errorItem
+ }
+ return undefined
+ }
+ }
+ Component{
+ id:com_loading
+ FluArea{
+ paddings: 0
+ border.width: 0
+ radius: 0
+ color:control.color
+ ColumnLayout{
+ anchors.centerIn: parent
+ FluProgressRing{
+ indeterminate: true
+ Layout.alignment: Qt.AlignHCenter
+ }
+ FluText{
+ text:control.loadingText
+ Layout.alignment: Qt.AlignHCenter
+ }
+ }
+ }
+ }
+ Component {
+ id:com_empty
+ FluArea{
+ paddings: 0
+ border.width: 0
+ radius: 0
+ color:control.color
+ ColumnLayout{
+ anchors.centerIn: parent
+ FluText{
+ text:control.emptyText
+ font: FluTextStyle.BodyStrong
+ Layout.alignment: Qt.AlignHCenter
+ }
+ }
+ }
+ }
+ Component{
+ id:com_error
+ FluArea{
+ paddings: 0
+ border.width: 0
+ radius: 0
+ color:control.color
+ ColumnLayout{
+ anchors.centerIn: parent
+ FluText{
+ text:control.errorText
+ font: FluTextStyle.BodyStrong
+ Layout.alignment: Qt.AlignHCenter
+ }
+ FluFilledButton{
+ id:btn_error
+ Layout.alignment: Qt.AlignHCenter
+ text:control.errorButtonText
+ onClicked:{
+ control.errorClicked()
+ }
+ }
+ }
+ }
+ }
+ function showSuccessView(){
+ statusMode = FluStatusLayoutType.Success
+ }
+ function showLoadingView(){
+ statusMode = FluStatusLayoutType.Loading
+ }
+ function showEmptyView(){
+ statusMode = FluStatusLayoutType.Empty
+ }
+ function showErrorView(){
+ statusMode = FluStatusLayoutType.Error
+ }
+}
diff --git a/src/FluentUI/Controls/FluTabView.qml b/src/FluentUI/Controls/FluTabView.qml
new file mode 100644
index 00000000..dab42f31
--- /dev/null
+++ b/src/FluentUI/Controls/FluTabView.qml
@@ -0,0 +1,299 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import FluentUI
+
+Item {
+ property int tabWidthBehavior : FluTabViewType.Equal
+ property int closeButtonVisibility : FluTabViewType.Always
+ property int itemWidth: 146
+ property bool addButtonVisibility: true
+ signal newPressed
+ id:control
+ implicitHeight: height
+ implicitWidth: width
+ anchors.fill: {
+ if(parent)
+ return parent
+ return undefined
+ }
+ QtObject {
+ id: d
+ property int dragIndex: -1
+ property bool dragBehavior: false
+ property bool itemPress: false
+ property int maxEqualWidth: 240
+ }
+ MouseArea{
+ anchors.fill: parent
+ preventStealing: true
+ }
+ ListModel{
+ id:tab_model
+ }
+ FluIconButton{
+ id:btn_new
+ visible: addButtonVisibility
+ width: 34
+ height: 34
+ x:Math.min(tab_nav.contentWidth,tab_nav.width)
+ anchors.top: parent.top
+ iconSource: FluentIcons.Add
+ onClicked: {
+ newPressed()
+ }
+ }
+ ListView{
+ id:tab_nav
+ height: 34
+ orientation: ListView.Horizontal
+ anchors{
+ top: parent.top
+ left: parent.left
+ right: parent.right
+ rightMargin: 34
+ }
+ interactive: false
+ model: tab_model
+ move: Transition {
+ NumberAnimation { properties: "x"; duration: 100; easing.type: Easing.OutCubic }
+ NumberAnimation { properties: "y"; duration: 100; easing.type: Easing.OutCubic }
+ }
+ moveDisplaced: Transition {
+ NumberAnimation { properties: "x"; duration: 300; easing.type: Easing.OutCubic}
+ NumberAnimation { properties: "y"; duration: 100; easing.type: Easing.OutCubic }
+ }
+ clip: true
+ ScrollBar.horizontal: ScrollBar{
+ id: scroll_nav
+ policy: ScrollBar.AlwaysOff
+ }
+ delegate: Item{
+ width: item_layout.width
+ height: item_container.height
+ z: item_mouse_drag.pressed ? 1000 : 1
+ Item{
+ id:item_layout
+ width: item_container.width
+ height: item_container.height
+ Item{
+ id:item_container
+ property real timestamp: new Date().getTime()
+ height: tab_nav.height
+ width: {
+ if(tabWidthBehavior === FluTabViewType.Equal){
+ return Math.max(Math.min(d.maxEqualWidth,tab_nav.width/tab_nav.count),41 + item_btn_close.width)
+ }
+ if(tabWidthBehavior === FluTabViewType.SizeToContent){
+ return itemWidth
+ }
+ if(tabWidthBehavior === FluTabViewType.Compact){
+ return item_mouse_hove.containsMouse || item_btn_close.hovered || tab_nav.currentIndex === index ? itemWidth : 41 + item_btn_close.width
+ }
+ return Math.max(Math.min(d.maxEqualWidth,tab_nav.width/tab_nav.count),41 + item_btn_close.width)
+ }
+ Behavior on x { enabled: d.dragBehavior; NumberAnimation { duration: 200 } }
+ Behavior on y { enabled: d.dragBehavior; NumberAnimation { duration: 200 } }
+ MouseArea{
+ id:item_mouse_hove
+ anchors.fill: parent
+ hoverEnabled: true
+ }
+ FluTooltip{
+ visible: item_mouse_hove.containsMouse
+ text:item_text.text
+ delay: 1000
+ }
+ MouseArea{
+ id:item_mouse_drag
+ anchors.fill: parent
+ drag.target: item_container
+ drag.axis: Drag.XAxis
+ onWheel: (wheel)=>{
+ if (wheel.angleDelta.y > 0) scroll_nav.decrease()
+ else scroll_nav.increase()
+ }
+ onPressed: {
+ d.itemPress = true
+ item_container.timestamp = new Date().getTime();
+ d.dragBehavior = false;
+ var pos = tab_nav.mapFromItem(item_container, 0, 0)
+ d.dragIndex = model.index
+ item_container.parent = tab_nav
+ item_container.x = pos.x
+ item_container.y = pos.y
+ }
+ onReleased: {
+ d.itemPress = false
+ timer.stop()
+ var timeDiff = new Date().getTime() - item_container.timestamp
+ if (timeDiff < 300) {
+ tab_nav.currentIndex = index
+ }
+ d.dragIndex = -1;
+ var pos = tab_nav.mapToItem(item_layout, item_container.x, item_container.y)
+ item_container.parent = item_layout;
+ item_container.x = pos.x;
+ item_container.y = pos.y;
+ d.dragBehavior = true;
+ item_container.x = 0;
+ item_container.y = 0;
+ }
+ onPositionChanged: {
+ var pos = tab_nav.mapFromItem(item_container, 0, 0)
+ updatePosition(pos)
+ if(pos.x<0){
+ timer.isIncrease = false
+ timer.restart()
+ }else if(pos.x>tab_nav.width-itemWidth){
+ timer.isIncrease = true
+ timer.restart()
+ }else{
+ timer.stop()
+ }
+ }
+ Timer{
+ id:timer
+ property bool isIncrease: true
+ interval: 10
+ repeat: true
+ onTriggered: {
+ if(isIncrease){
+ if(tab_nav.contentX>=tab_nav.contentWidth-tab_nav.width){
+ return
+ }
+ tab_nav.contentX = tab_nav.contentX+2
+ }else{
+ if(tab_nav.contentX<=0){
+ return
+ }
+ tab_nav.contentX = tab_nav.contentX-2
+ }
+ item_mouse_drag.updatePosition(tab_nav.mapFromItem(item_container, 0, 0))
+ }
+ }
+ function updatePosition(pos){
+ var idx = tab_nav.indexAt(pos.x+tab_nav.contentX+1, pos.y)
+ if(idx<0){
+ return
+ }
+ if(idx>=tab_nav.count){
+ return
+ }
+ if (d.dragIndex !== idx) {
+ tab_model.move(d.dragIndex, idx, 1)
+ d.dragIndex = idx;
+ }
+ }
+ }
+ FluRectangle{
+ anchors.fill: parent
+ radius: [6,6,0,0]
+ color: {
+ if(item_mouse_hove.containsMouse || item_btn_close.hovered){
+ return FluTheme.itemHoverColor
+ }
+ if(tab_nav.currentIndex === index){
+ return FluTheme.itemCheckColor
+ }
+ return FluTheme.itemNormalColor
+ }
+ }
+ RowLayout{
+ spacing: 0
+ height: parent.height
+ Image{
+ source:model.icon
+ Layout.leftMargin: 10
+ Layout.preferredWidth: 14
+ Layout.preferredHeight: 14
+ Layout.alignment: Qt.AlignVCenter
+ }
+ FluText{
+ id:item_text
+ text: model.text
+ Layout.leftMargin: 10
+ visible: {
+ if(tabWidthBehavior === FluTabViewType.Equal){
+ return true
+ }
+ if(tabWidthBehavior === FluTabViewType.SizeToContent){
+ return true
+ }
+ if(tabWidthBehavior === FluTabViewType.Compact){
+ return item_mouse_hove.containsMouse || item_btn_close.hovered || tab_nav.currentIndex === index
+ }
+ return false
+ }
+ Layout.preferredWidth: visible?item_container.width - 41 - item_btn_close.width:0
+ elide: Text.ElideRight
+ Layout.alignment: Qt.AlignVCenter
+ }
+ }
+ FluIconButton{
+ id:item_btn_close
+ iconSource: FluentIcons.ChromeClose
+ iconSize: 10
+ width: visible ? 24 : 0
+ height: 24
+ visible: {
+ if(closeButtonVisibility === FluTabViewType.Never)
+ return false
+ if(closeButtonVisibility === FluTabViewType.OnHover)
+ return item_mouse_hove.containsMouse || item_btn_close.hovered
+ return true
+ }
+ anchors{
+ right: parent.right
+ rightMargin: 5
+ verticalCenter: parent.verticalCenter
+ }
+ onClicked: {
+ tab_model.remove(index)
+ }
+ }
+ FluDivider{
+ width: 1
+ height: 16
+ orientation: Qt.Vertical
+ anchors{
+ verticalCenter: parent.verticalCenter
+ right: parent.right
+ }
+ }
+ }
+ }
+ }
+ }
+ Item{
+ id:container
+ anchors{
+ top: tab_nav.bottom
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ }
+ Repeater{
+ model:tab_model
+ FluLoader{
+ property var argument: model.argument
+ anchors.fill: parent
+ sourceComponent: model.page
+ visible: tab_nav.currentIndex === index
+ }
+ }
+ }
+ function createTab(icon,text,page,argument={}){
+ return {icon:icon,text:text,page:page,argument:argument}
+ }
+ function appendTab(icon,text,page,argument){
+ tab_model.append(createTab(icon,text,page,argument))
+ }
+ function setTabList(list){
+ tab_model.clear()
+ tab_model.append(list)
+ }
+ function count(){
+ return tab_nav.count
+ }
+}
diff --git a/src/FluentUI/Controls/FluTableView.qml b/src/FluentUI/Controls/FluTableView.qml
new file mode 100644
index 00000000..2acda1f5
--- /dev/null
+++ b/src/FluentUI/Controls/FluTableView.qml
@@ -0,0 +1,849 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Basic
+import QtQuick.Layouts
+import Qt.labs.qmlmodels
+import FluentUI
+
+Rectangle {
+ property var columnSource
+ property var dataSource
+ property color borderColor: FluTheme.dark ? "#252525" : "#e4e4e4"
+ property alias rows: table_view.rows
+ property alias columns: table_view.columns
+ property bool horizonalHeaderVisible: true
+ property bool verticalHeaderVisible: true
+ property color selectedBorderColor: FluTheme.primaryColor
+ property color selectedColor: FluTools.colorAlpha(FluTheme.primaryColor,0.3)
+ property alias sourceModel: table_model
+ id:control
+ color: FluTheme.dark ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1)
+ onColumnSourceChanged: {
+ if(columnSource.length!==0){
+ var columns= []
+ var columnsData = []
+ var headerRow = {}
+ columnSource.forEach(function(item){
+ var column = Qt.createQmlObject('import Qt.labs.qmlmodels 1.0;TableModelColumn{}',table_model);
+ column.display = item.dataIndex
+ columnsData.push(item)
+ columns.push(column)
+ headerRow[item.dataIndex] = item.title
+ })
+ d.columns_data = columnsData
+ table_model.columns = columns
+ header_column_model.columns = columns
+ header_column_model.rows = [headerRow]
+ }
+ }
+ QtObject{
+ id:d
+ property var current
+ property int rowHoverIndex: -1
+ property int defaultItemWidth: 100
+ property int defaultItemHeight: 42
+ property var columns_data: []
+ property var editDelegate
+ property var editPosition
+ function getEditDelegate(column){
+ var obj =d.columns_data[column].editDelegate
+ if(obj){
+ return obj
+ }
+ if(d.columns_data[column].editMultiline === true){
+ return com_edit_multiline
+ }
+ return com_edit
+ }
+ }
+ onDataSourceChanged: {
+ table_model.clear()
+ table_model.rows = dataSource
+ }
+ TableModel {
+ id:table_model
+ TableModelColumn {}
+ }
+ TableModel{
+ id:header_column_model
+ TableModelColumn {}
+ }
+ TableModel{
+ id:header_row_model
+ TableModelColumn { display: "rowIndex" }
+ }
+ FluTableSortProxyModel{
+ id:table_sort_model
+ model: table_model
+ }
+ Component{
+ id:com_edit
+ FluTextBox{
+ id:text_box
+ text: String(display)
+ readOnly: true === d.columns_data[column].readOnly
+ Component.onCompleted: {
+ forceActiveFocus()
+ selectAll()
+ }
+ onCommit: {
+ if(!readOnly){
+ editTextChaged(text_box.text)
+ }
+ tableView.closeEditor()
+ }
+ }
+ }
+ Component{
+ id:com_edit_multiline
+ Item{
+ anchors.fill: parent
+ ScrollView{
+ id:item_scroll
+ clip: true
+ anchors.fill: parent
+ ScrollBar.vertical: FluScrollBar{
+ parent: item_scroll
+ x: item_scroll.mirrored ? 0 : item_scroll.width - width
+ y: item_scroll.topPadding
+ height: item_scroll.availableHeight
+ active: item_scroll.ScrollBar.horizontal.active
+ }
+ FluMultilineTextBox {
+ id:text_box
+ text: display
+ readOnly: true === d.columns_data[column].readOnly
+ verticalAlignment: TextInput.AlignVCenter
+ Component.onCompleted: {
+ forceActiveFocus()
+ selectAll()
+ }
+ rightPadding: 24
+ onCommit: {
+ if(!readOnly){
+ editTextChaged(text_box.text)
+ }
+ tableView.closeEditor()
+ }
+ }
+ }
+ FluIconButton{
+ iconSource:FluentIcons.ChromeClose
+ iconSize: 10
+ width: 20
+ height: 20
+ visible: {
+ if(text_box.readOnly)
+ return false
+ return text_box.text !== ""
+ }
+ anchors{
+ verticalCenter: parent.verticalCenter
+ right: parent.right
+ rightMargin: 5
+ }
+ onClicked:{
+ text_box.text = ""
+ }
+ }
+ }
+ }
+ Component{
+ id:com_text
+ FluText {
+ id:item_text
+ text: String(display)
+ elide: Text.ElideRight
+ wrapMode: Text.WrapAnywhere
+ anchors{
+ fill: parent
+ leftMargin: 11
+ rightMargin: 11
+ topMargin: 6
+ bottomMargin: 6
+ }
+ verticalAlignment: Text.AlignVCenter
+ MouseArea{
+ acceptedButtons: Qt.NoButton
+ id: hover_handler
+ hoverEnabled: true
+ anchors.fill: parent
+ }
+ FluTooltip{
+ text: item_text.text
+ delay: 500
+ visible: item_text.contentWidth < item_text.implicitWidth && item_text.contentHeight < item_text.implicitHeight && hover_handler.containsMouse
+ }
+ }
+ }
+ Component{
+ id:com_table_delegate
+ MouseArea{
+ id:item_table_mouse
+ property var rowObject : control.getRow(row)
+ property var itemModel: model
+ property bool editVisible: {
+ if(d.editPosition === undefined){
+ return false
+ }
+ if(d.editPosition._key === rowObject._key && d.editPosition.column === column){
+ return true
+ }
+ return false
+ }
+ hoverEnabled: true
+ onEntered: {
+ d.rowHoverIndex = row
+ }
+ onWidthChanged: {
+ if(editVisible){
+ updateEditPosition()
+ }
+ }
+ onHeightChanged: {
+ if(editVisible){
+ updateEditPosition()
+ }
+ }
+ onXChanged: {
+ if(editVisible){
+ updateEditPosition()
+ }
+ }
+ onYChanged: {
+ if(editVisible){
+ updateEditPosition()
+ }
+ }
+ function updateEditPosition(){
+ var obj = {}
+ obj._key = rowObject._key
+ obj.column = column
+ obj.row = row
+ obj.x = item_table_mouse.x
+ obj.y = item_table_mouse.y + 1
+ obj.width = item_table_mouse.width
+ obj.height = item_table_mouse.height - 2
+ d.editPosition = obj
+ }
+ Rectangle{
+ id:item_table
+ anchors.fill: parent
+ property point position: Qt.point(column,row)
+ property bool isRowSelected: {
+ if(rowObject === null)
+ return false
+ if(d.current){
+ return rowObject._key === d.current._key
+ }
+ return false
+ }
+ color:{
+ if(item_table.isRowSelected){
+ return control.selectedColor
+ }
+ if(d.rowHoverIndex === row || item_table.isRowSelected){
+ return FluTheme.dark ? Qt.rgba(1,1,1,0.06) : Qt.rgba(0,0,0,0.06)
+ }
+ return (row%2!==0) ? control.color : (FluTheme.dark ? Qt.rgba(1,1,1,0.015) : Qt.rgba(0,0,0,0.015))
+ }
+ MouseArea{
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton
+ onPressed:{
+ closeEditor()
+ }
+ onCanceled: {
+ }
+ onReleased: {
+ }
+ onDoubleClicked:{
+ if(typeof(display) == "object"){
+ return
+ }
+ d.editDelegate = d.getEditDelegate(column)
+ updateEditPosition()
+ loader_edit.display = display
+ }
+ onClicked:
+ (event)=>{
+ d.current = rowObject
+ closeEditor()
+ event.accepted = true
+ }
+ }
+ FluLoader{
+ property var model: itemModel
+ property var display: itemModel.display
+ property int row: item_table.position.y
+ property int column: item_table.position.x
+ property bool isObject: typeof(display) == "object"
+ property var options: {
+ if(isObject){
+ return display.options
+ }
+ return {}
+ }
+ anchors.fill: parent
+ sourceComponent: {
+ if(isObject){
+ return display.comId
+ }
+ return com_text
+ }
+ }
+ Item{
+ anchors.fill: parent
+ visible: item_table.isRowSelected
+ Rectangle{
+ width: 1
+ height: parent.height
+ anchors.left: parent.left
+ color: control.selectedBorderColor
+ visible: column === 0
+ }
+ Rectangle{
+ width: 1
+ height: parent.height
+ anchors.right: parent.right
+ color: control.selectedBorderColor
+ visible: column === control.columns-1
+ }
+ Rectangle{
+ width: parent.width
+ height: 1
+ anchors.top: parent.top
+ color: control.selectedBorderColor
+ }
+ Rectangle{
+ width: parent.width
+ height: 1
+ anchors.bottom: parent.bottom
+ color: control.selectedBorderColor
+ }
+ }
+ }
+ }
+ }
+ MouseArea{
+ id:layout_mouse_table
+ hoverEnabled: true
+ anchors{
+ left: header_vertical.right
+ top: header_horizontal.bottom
+ right: parent.right
+ bottom: parent.bottom
+ }
+ onExited: {
+ d.rowHoverIndex = -1
+ }
+ onCanceled: {
+ d.rowHoverIndex = -1
+ }
+ TableView {
+ id:table_view
+ ListModel{
+ id:model_columns
+ }
+ boundsBehavior: Flickable.StopAtBounds
+ syncView: header_horizontal
+ syncDirection: Qt.Horizontal
+ anchors.fill: parent
+ ScrollBar.vertical:scroll_bar_v
+ 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
+ clip: true
+ onRowsChanged: {
+ closeEditor()
+ }
+ delegate: com_table_delegate
+ FluLoader{
+ id:loader_edit
+ property var tableView: control
+ property var display
+ property int column: {
+ if(d.editPosition){
+ return d.editPosition.column
+ }
+ return 0
+ }
+ property int row: {
+ if(d.editPosition){
+ return d.editPosition.row
+ }
+ return 0
+ }
+ signal editTextChaged(string text)
+ sourceComponent: d.editPosition ? d.editDelegate : undefined
+ onEditTextChaged:
+ (text)=>{
+ var obj = control.getRow(row)
+ obj[d.columns_data[column].dataIndex] = text
+ control.setRow(row,obj)
+ }
+ width: {
+ if(d.editPosition){
+ return d.editPosition.width
+ }
+ return 0
+ }
+ height: {
+ if(d.editPosition){
+ return d.editPosition.height
+ }
+ return 0
+ }
+ x:{
+ if(d.editPosition){
+ return d.editPosition.x
+ }
+ return 0
+ }
+ y:{
+ if(d.editPosition){
+ return d.editPosition.y
+ }
+ return 0
+ }
+ z:999
+ }
+ }
+ }
+ Component{
+ id:com_column_header_delegate
+ Rectangle{
+ id:column_item_control
+ readonly property real cellPadding: 8
+ property bool canceled: false
+ property int columnIndex: column
+ readonly property var columnObject : d.columns_data[column]
+ implicitWidth: {
+ return (item_column_loader.item && item_column_loader.item.implicitWidth) + (cellPadding * 2)
+ }
+ implicitHeight: Math.max(36, (item_column_loader.item&&item_column_loader.item.implicitHeight) + (cellPadding * 2))
+ color: FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(247/255,247/255,247/255,1)
+ Rectangle{
+ border.color: control.borderColor
+ width: parent.width
+ height: 1
+ anchors.top: parent.top
+ color:"#00000000"
+ }
+ Rectangle{
+ border.color: control.borderColor
+ width: parent.width
+ height: 1
+ anchors.bottom: parent.bottom
+ color:"#00000000"
+ }
+ Rectangle{
+ border.color: control.borderColor
+ width: 1
+ height: parent.height
+ anchors.left: parent.left
+ visible: column !== 0
+ color:"#00000000"
+ }
+ Rectangle{
+ border.color: control.borderColor
+ width: 1
+ height: parent.height
+ anchors.right: parent.right
+ color:"#00000000"
+ visible: column === table_view.columns - 1
+ }
+ MouseArea{
+ id:column_item_control_mouse
+ anchors.fill: parent
+ anchors.rightMargin: 6
+ hoverEnabled: true
+ onCanceled: {
+ column_item_control.canceled = true
+ }
+ onContainsMouseChanged: {
+ if(!containsMouse){
+ column_item_control.canceled = false
+ }
+ }
+ onClicked:
+ (event)=>{
+ closeEditor()
+ }
+ }
+ FluLoader{
+ id:item_column_loader
+ property var itemModel: model
+ property var modelData: model.display
+ property var tableView: table_view
+ property var tableModel: table_model
+ property var options:{
+ if(typeof(modelData) == "object"){
+ return modelData.options
+ }
+ return {}
+ }
+ property int column: column_item_control.columnIndex
+ width: parent.width
+ height: parent.height
+ sourceComponent: {
+ if(typeof(modelData) == "object"){
+ return modelData.comId
+ }
+ return com_column_text
+ }
+ }
+ MouseArea{
+ property point clickPos: "0,0"
+ height: parent.height
+ width: 6
+ anchors.right: parent.right
+ acceptedButtons: Qt.LeftButton
+ hoverEnabled: true
+ visible: !(columnObject.width === columnObject.minimumWidth && columnObject.width === columnObject.maximumWidth && columnObject.width)
+ cursorShape: Qt.SplitHCursor
+ preventStealing: true
+ onPressed :
+ (mouse)=>{
+ FluTools.setOverrideCursor(Qt.SplitHCursor)
+ clickPos = Qt.point(mouse.x, mouse.y)
+ }
+ onReleased:{
+ FluTools.restoreOverrideCursor()
+ }
+ onCanceled: {
+ FluTools.restoreOverrideCursor()
+ }
+ onPositionChanged:
+ (mouse)=>{
+ if(!pressed){
+ return
+ }
+ var delta = Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y)
+ var minimumWidth = columnObject.minimumWidth
+ var maximumWidth = columnObject.maximumWidth
+ var w = columnObject.width
+ if(!w){
+ w = d.defaultItemWidth
+ }
+ if(!minimumWidth){
+ minimumWidth = d.defaultItemWidth
+ }
+ if(!maximumWidth){
+ maximumWidth = 65535
+ }
+ columnObject.width = Math.min(Math.max(minimumWidth, w + delta.x),maximumWidth)
+ header_horizontal.forceLayout()
+ }
+ }
+ }
+ }
+ Component{
+ id:com_row_header_delegate
+ Rectangle{
+ id:item_control
+ readonly property real cellPadding: 8
+ property bool canceled: false
+ property var rowObject: control.getRow(row)
+ implicitWidth: Math.max(30, row_text.implicitWidth + (cellPadding * 2))
+ implicitHeight: row_text.implicitHeight + (cellPadding * 2)
+ color: FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(247/255,247/255,247/255,1)
+ Rectangle{
+ border.color: control.borderColor
+ width: parent.width
+ height: 1
+ anchors.top: parent.top
+ visible: row !== 0
+ color:"#00000000"
+ }
+ Rectangle{
+ border.color: control.borderColor
+ width: parent.width
+ height: 1
+ anchors.bottom: parent.bottom
+ visible: row === table_view.rows - 1
+ color:"#00000000"
+ }
+ Rectangle{
+ border.color: control.borderColor
+ width: 1
+ height: parent.height
+ anchors.left: parent.left
+ color:"#00000000"
+ }
+ Rectangle{
+ border.color: control.borderColor
+ width: 1
+ height: parent.height
+ anchors.right: parent.right
+ color:"#00000000"
+ }
+ FluText{
+ id:row_text
+ anchors.centerIn: parent
+ text: model.display
+ }
+ MouseArea{
+ id:item_control_mouse
+ anchors.fill: parent
+ anchors.bottomMargin: 6
+ hoverEnabled: true
+ onCanceled: {
+ item_control.canceled = true
+ }
+ onContainsMouseChanged: {
+ if(!containsMouse){
+ item_control.canceled = false
+ }
+ }
+ onClicked:
+ (event)=>{
+ closeEditor()
+ }
+ }
+ MouseArea{
+ property point clickPos: "0,0"
+ height: 6
+ width: parent.width
+ anchors.bottom: parent.bottom
+ acceptedButtons: Qt.LeftButton
+ cursorShape: Qt.SplitVCursor
+ preventStealing: true
+ visible: {
+ if(rowObject === null)
+ return false
+ return !(rowObject.height === rowObject._minimumHeight && rowObject.height === rowObject._maximumHeight && rowObject.height)
+ }
+ onPressed :
+ (mouse)=>{
+ FluTools.setOverrideCursor(Qt.SplitVCursor)
+ clickPos = Qt.point(mouse.x, mouse.y)
+ }
+ onReleased:{
+ FluTools.restoreOverrideCursor()
+ }
+ onCanceled: {
+ FluTools.restoreOverrideCursor()
+ }
+ onPositionChanged:
+ (mouse)=>{
+ if(!pressed){
+ return
+ }
+ var rowObject = control.getRow(row)
+ var delta = Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y)
+ var minimumHeight = rowObject._minimumHeight
+ var maximumHeight = rowObject._maximumHeight
+ var h = rowObject.height
+ if(!h){
+ h = d.defaultItemHeight
+ }
+ if(!minimumHeight){
+ minimumHeight = d.defaultItemHeight
+ }
+ if(!maximumHeight){
+ maximumHeight = 65535
+ }
+ rowObject.height = Math.min(Math.max(minimumHeight, h + delta.y),maximumHeight)
+ control.setRow(row,rowObject)
+ table_view.forceLayout()
+ }
+ }
+ }
+ }
+ Component{
+ id:com_column_text
+ FluText {
+ id: column_text
+ text: modelData
+ anchors.fill: parent
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+ }
+ Item{
+ id: header_vertical_column
+ anchors{
+ top: header_horizontal.top
+ bottom: header_horizontal.bottom
+ left: parent.left
+ right: header_vertical.right
+ }
+ Rectangle{
+ border.color: control.borderColor
+ width: parent.width
+ height: 1
+ anchors.top: parent.top
+ color:"#00000000"
+ }
+ Rectangle{
+ border.color: control.borderColor
+ width: parent.width
+ height: 1
+ anchors.bottom: parent.bottom
+ color:"#00000000"
+ }
+ Rectangle{
+ border.color: control.borderColor
+ width: 1
+ height: parent.height
+ anchors.left: parent.left
+ color:"#00000000"
+ }
+ Rectangle{
+ border.color: control.borderColor
+ width: 1
+ height: parent.height
+ anchors.right: parent.right
+ color:"#00000000"
+ }
+ }
+ TableView {
+ id: header_horizontal
+ model: header_column_model
+ anchors{
+ left: header_vertical.right
+ right: layout_mouse_table.right
+ top: parent.top
+ }
+ visible: control.horizonalHeaderVisible
+ height: visible ? Math.max(1, contentHeight) : 0
+ boundsBehavior: Flickable.StopAtBounds
+ clip: true
+ ScrollBar.horizontal:scroll_bar_h
+ columnWidthProvider: function(column) {
+ var columnObject = d.columns_data[column]
+ var width = columnObject.width
+ if(width){
+ return width
+ }
+ var minimumWidth = columnObject.minimumWidth
+ if(minimumWidth){
+ return minimumWidth
+ }
+ return d.defaultItemWidth
+ }
+ onContentXChanged:{
+ timer_horizontal_force_layout.restart()
+ }
+ Timer{
+ id:timer_horizontal_force_layout
+ interval: 50
+ onTriggered: {
+ header_horizontal.forceLayout()
+ }
+ }
+ delegate: com_column_header_delegate
+ }
+ TableView {
+ id: header_vertical
+ boundsBehavior: Flickable.StopAtBounds
+ anchors{
+ top: layout_mouse_table.top
+ left: parent.left
+ }
+ visible: control.verticalHeaderVisible
+ implicitWidth: visible ? Math.max(1, contentWidth) : 0
+ implicitHeight: syncView ? syncView.height : 0
+ syncDirection: Qt.Vertical
+ syncView: table_view
+ clip: true
+ model: header_row_model
+ Connections{
+ target: table_model
+ function onRowCountChanged(){
+ header_row_model.rows = Array.from({length: table_model.rows.length}, (_, i) => ({rowIndex:i+1}))
+ }
+ }
+ onContentYChanged:{
+ timer_vertical_force_layout.restart()
+ }
+ Timer{
+ id:timer_vertical_force_layout
+ interval: 50
+ onTriggered: {
+ header_vertical.forceLayout()
+ }
+ }
+ delegate: com_row_header_delegate
+ }
+ FluScrollBar {
+ id:scroll_bar_h
+ anchors{
+ left: layout_mouse_table.left
+ right: parent.right
+ bottom: layout_mouse_table.bottom
+ }
+ z:999
+ }
+ FluScrollBar {
+ id:scroll_bar_v
+ anchors{
+ top: layout_mouse_table.top
+ bottom: layout_mouse_table.bottom
+ right: parent.right
+ }
+ z:999
+ }
+ function closeEditor(){
+ d.editPosition = undefined
+ d.editDelegate = undefined
+ }
+ function resetPosition(){
+ scroll_bar_h.position = 0
+ scroll_bar_v.position = 0
+ }
+ function customItem(comId,options={}){
+ var o = {}
+ o.comId = comId
+ o.options = options
+ return o
+ }
+ function sort(callback=undefined){
+ if(callback){
+ table_sort_model.setComparator(function(left,right){
+ return callback(table_model.getRow(left),table_model.getRow(right))
+ })
+ }else{
+ table_sort_model.setComparator(undefined)
+ }
+ }
+ function filter(callback=undefined){
+ if(callback){
+ table_sort_model.setFilter(function(index){
+ return callback(table_model.getRow(index))
+ })
+ }else{
+ table_sort_model.setFilter(undefined)
+ }
+ }
+ function setRow(rowIndex,obj){
+ if(rowIndex>=0 && rowIndex=0 && rowIndex=0 && rowIndex d.handleCommit(event)
+ Keys.onReturnPressed:(event)=> d.handleCommit(event)
+ QtObject{
+ id:d
+ function handleCommit(event){
+ control.commit(control.text)
+ }
+ }
+ MouseArea{
+ anchors.fill: parent
+ cursorShape: Qt.IBeamCursor
+ acceptedButtons: Qt.RightButton
+ onClicked: {
+ if(control.echoMode === TextInput.Password){
+ return
+ }
+ if(control.readOnly && control.text === ""){
+ return
+ }
+ menu.popup()
+ }
+ }
+ RowLayout{
+ height: parent.height
+ anchors{
+ right: parent.right
+ rightMargin: 5
+ }
+ spacing: 4
+ FluIconButton{
+ iconSource: FluentIcons.Cancel
+ iconSize: 12
+ Layout.preferredWidth: 30
+ Layout.preferredHeight: 20
+ Layout.alignment: Qt.AlignVCenter
+ iconColor: FluTheme.dark ? Qt.rgba(222/255,222/255,222/255,1) : Qt.rgba(97/255,97/255,97/255,1)
+ verticalPadding: 0
+ horizontalPadding: 0
+ visible: {
+ if(control.cleanEnabled === false){
+ return false
+ }
+ if(control.readOnly)
+ return false
+ return control.text !== ""
+ }
+ contentDescription:"Clean"
+ onClicked:{
+ control.clear()
+ }
+ }
+ FluIcon{
+ id:icon_end
+ iconSource: control.iconSource
+ iconSize: 12
+ Layout.alignment: Qt.AlignVCenter
+ Layout.rightMargin: 7
+ iconColor: FluTheme.dark ? Qt.rgba(222/255,222/255,222/255,1) : Qt.rgba(97/255,97/255,97/255,1)
+ visible: control.iconSource != 0
+ }
+ }
+ FluTextBoxMenu{
+ id:menu
+ inputItem: control
+ }
+}
diff --git a/src/FluentUI/Controls/FluTextBoxBackground.qml b/src/FluentUI/Controls/FluTextBoxBackground.qml
new file mode 100644
index 00000000..3ec71baf
--- /dev/null
+++ b/src/FluentUI/Controls/FluTextBoxBackground.qml
@@ -0,0 +1,56 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+FluClip{
+ property Item inputItem
+ property int borderWidth: 1
+ id:control
+ radius: [4,4,4,4]
+ Rectangle{
+ radius: 4
+ anchors.fill: parent
+ color: {
+ if(inputItem && inputItem.disabled){
+ return FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(252/255,252/255,252/255,1)
+ }
+ if(inputItem && inputItem.activeFocus){
+ return FluTheme.dark ? Qt.rgba(36/255,36/255,36/255,1) : Qt.rgba(1,1,1,1)
+ }
+ if(inputItem && inputItem.hovered){
+ return FluTheme.dark ? Qt.rgba(68/255,68/255,68/255,1) : Qt.rgba(251/255,251/255,251/255,1)
+ }
+ return FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(1,1,1,1)
+ }
+ border.width: control.borderWidth
+ border.color: {
+ if(inputItem && inputItem.disabled){
+ return FluTheme.dark ? Qt.rgba(73/255,73/255,73/255,1) : Qt.rgba(237/255,237/255,237/255,1)
+ }
+ return FluTheme.dark ? Qt.rgba(76/255,76/255,76/255,1) : Qt.rgba(240/255,240/255,240/255,1)
+ }
+ }
+ Rectangle{
+ width: parent.width
+ height: inputItem && inputItem.activeFocus ? 2 : 1
+ anchors.bottom: parent.bottom
+ visible: !(inputItem && inputItem.disabled)
+ color: {
+ if(inputItem && inputItem.activeFocus){
+ return FluTheme.primaryColor
+ }
+ if(FluTheme.dark){
+ return Qt.rgba(166/255,166/255,166/255,1)
+ }else{
+ return Qt.rgba(134/255,134/255,134/255,1)
+ }
+ }
+ Behavior on height{
+ enabled: FluTheme.enableAnimation
+ NumberAnimation{
+ duration: 83
+ easing.type: Easing.OutCubic
+ }
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluTextBoxMenu.qml b/src/FluentUI/Controls/FluTextBoxMenu.qml
new file mode 100644
index 00000000..3be0c1b2
--- /dev/null
+++ b/src/FluentUI/Controls/FluTextBoxMenu.qml
@@ -0,0 +1,104 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+FluMenu{
+ property string cutText : qsTr("Cut")
+ property string copyText : qsTr("Copy")
+ property string pasteText : qsTr("Paste")
+ property string selectAllText : qsTr("Select All")
+ property var inputItem
+ id:menu
+ enableAnimation: false
+ width: 120
+ focus: false
+ onVisibleChanged: {
+ if(inputItem){
+ inputItem.forceActiveFocus()
+ }
+ }
+ Connections{
+ target: {
+ if(inputItem){
+ return inputItem
+ }
+ return null
+ }
+ function onTextChanged() {
+ menu.close()
+ }
+ function onActiveFocusChanged() {
+ if(!inputItem.activeFocus){
+ menu.close()
+ }
+ }
+ }
+ FluIconButton{
+ display: Button.TextOnly
+ text:cutText
+ focus: false
+ padding: 0
+ height: visible ? 36 : 0
+ visible: {
+ if(inputItem){
+ return inputItem.selectedText !== "" && !inputItem.readOnly
+ }
+ return false
+ }
+ onClicked: {
+ inputItem.cut()
+ menu.close()
+ }
+ }
+ FluIconButton{
+ display: Button.TextOnly
+ text:copyText
+ focus: false
+ padding: 0
+ height: visible ? 36 : 0
+ visible: {
+ if(inputItem){
+ return inputItem.selectedText !== ""
+ }
+ return false
+ }
+ onClicked: {
+ inputItem.copy()
+ menu.close()
+ }
+ }
+ FluIconButton{
+ display: Button.TextOnly
+ text:pasteText
+ focus: false
+ padding: 0
+ visible: {
+ if(inputItem){
+ return !inputItem.readOnly
+ }
+ return false
+ }
+ height: visible ? 36 : 0
+ onClicked: {
+ inputItem.paste()
+ menu.close()
+ }
+ }
+ FluIconButton{
+ display: Button.TextOnly
+ text:selectAllText
+ focus: false
+ padding: 0
+ height: visible ? 36 : 0
+ visible: {
+ if(inputItem){
+ return inputItem.text !== ""
+ }
+ return false
+ }
+ onClicked: {
+ inputItem.selectAll()
+ menu.close()
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluTextButton.qml b/src/FluentUI/Controls/FluTextButton.qml
new file mode 100644
index 00000000..68d98129
--- /dev/null
+++ b/src/FluentUI/Controls/FluTextButton.qml
@@ -0,0 +1,65 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Basic
+import FluentUI
+
+Button {
+ property bool disabled: false
+ property string contentDescription: ""
+ property color normalColor: FluTheme.primaryColor
+ property color hoverColor: FluTheme.dark ? Qt.darker(normalColor,1.15) : Qt.lighter(normalColor,1.15)
+ property color pressedColor: FluTheme.dark ? Qt.darker(normalColor,1.3) : Qt.lighter(normalColor,1.3)
+ property color disableColor: FluTheme.dark ? Qt.rgba(82/255,82/255,82/255,1) : Qt.rgba(199/255,199/255,199/255,1)
+ property color backgroundHoverColor: FluTheme.itemHoverColor
+ property color backgroundPressedColor: FluTheme.itemPressColor
+ property color backgroundNormalColor: FluTheme.itemNormalColor
+ property color backgroundDisableColor: FluTheme.itemNormalColor
+ property bool textBold: true
+ property color textColor: {
+ if(!enabled){
+ return disableColor
+ }
+ if(pressed){
+ return pressedColor
+ }
+ return hovered ? hoverColor :normalColor
+ }
+ id: control
+ horizontalPadding:6
+ enabled: !disabled
+ font:FluTextStyle.Body
+ background: Rectangle{
+ implicitWidth: 28
+ implicitHeight: 28
+ radius: 4
+ color: {
+ if(!enabled){
+ return backgroundDisableColor
+ }
+ if(pressed){
+ return backgroundPressedColor
+ }
+ if(hovered){
+ return backgroundHoverColor
+ }
+ return backgroundNormalColor
+ }
+ FluFocusRectangle{
+ visible: control.visualFocus
+ radius:8
+ }
+ }
+ focusPolicy:Qt.TabFocus
+ Accessible.role: Accessible.Button
+ Accessible.name: control.text
+ Accessible.description: contentDescription
+ Accessible.onPressAction: control.clicked()
+ contentItem: FluText {
+ id:btn_text
+ text: control.text
+ font: control.font
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: control.textColor
+ }
+}
diff --git a/src/FluentUI/Controls/FluTimePicker.qml b/src/FluentUI/Controls/FluTimePicker.qml
new file mode 100644
index 00000000..f34b6dd6
--- /dev/null
+++ b/src/FluentUI/Controls/FluTimePicker.qml
@@ -0,0 +1,429 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Window
+import FluentUI
+
+Rectangle {
+ property color dividerColor: FluTheme.dark ? Qt.rgba(77/255,77/255,77/255,1) : Qt.rgba(239/255,239/255,239/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 normalColor: FluTheme.dark ? Qt.rgba(61/255,61/255,61/255,1) : Qt.rgba(254/255,254/255,254/255,1)
+ property int hourFormat: FluTimePickerType.H
+ property int isH: hourFormat === FluTimePickerType.H
+ property var current
+ property string amText: "上午"
+ property string pmText: "下午"
+ property string hourText: "时"
+ property string minuteText: "分"
+ property string cancelText: "取消"
+ property string okText: "确定"
+ signal accepted()
+ id:control
+ color: {
+ if(mouse_area.containsMouse){
+ return hoverColor
+ }
+ return normalColor
+ }
+ height: 30
+ width: 300
+ radius: 4
+ border.width: 1
+ border.color: dividerColor
+ Component.onCompleted: {
+ if(current){
+ var now = current;
+ var hour
+ var ampm;
+ if(isH){
+ hour = now.getHours();
+ if(hour>12){
+ ampm = control.pmText
+ hour = hour-12
+ }else{
+ ampm = control.amText
+ }
+ }else{
+ hour = now.getHours();
+ }
+ hour = text_hour.text === control.hourText ? hour.toString().padStart(2, '0') : text_hour.text
+ var minute = text_minute.text === control.minuteText ? now.getMinutes().toString().padStart(2, '0') : text_minute.text
+ ampm = text_ampm.text === "%1/%2".arg(control.amText).arg(control.pmText) ? ampm : text_ampm.text
+ text_hour.text = hour
+ text_minute.text = minute
+ if(isH){
+ text_ampm.text = ampm
+ }
+ }
+ }
+ Item{
+ id:d
+ property var window: Window.window
+ property bool changeFlag: true
+ property var rowData: ["","",""]
+ visible: false
+
+
+ }
+ MouseArea{
+ id: mouse_area
+ hoverEnabled: true
+ anchors.fill: parent
+ onClicked: {
+ popup.showPopup()
+ }
+ }
+ Rectangle{
+ id: divider_1
+ width: 1
+ x: isH ? parent.width/3 : parent.width/2
+ height: parent.height
+ color: dividerColor
+ }
+ Rectangle{
+ id: divider_2
+ width: 1
+ x: parent.width*2/3
+ height: parent.height
+ color: dividerColor
+ visible: isH
+ }
+ FluText{
+ id: text_hour
+ anchors{
+ left: parent.left
+ right: divider_1.left
+ top: parent.top
+ bottom: parent.bottom
+ }
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ text: control.hourText
+ }
+ FluText{
+ id: text_minute
+ anchors{
+ left: divider_1.right
+ right: isH ? divider_2.left : parent.right
+ top: parent.top
+ bottom: parent.bottom
+ }
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ text: control.minuteText
+ }
+ FluText{
+ id:text_ampm
+ visible: isH
+ anchors{
+ left: divider_2.right
+ right: parent.right
+ top: parent.top
+ bottom: parent.bottom
+ }
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ text: "%1/%2".arg(control.amText).arg(control.pmText)
+ }
+ Menu{
+ id:popup
+ width: container.width
+ height: container.height
+ modal: true
+ Overlay.modal: Item {}
+ enter: Transition {
+ reversible: true
+ NumberAnimation {
+ property: "opacity"
+ from:0
+ to:1
+ duration: FluTheme.enableAnimation ? 83 : 0
+ }
+ }
+ exit:Transition {
+ NumberAnimation {
+ property: "opacity"
+ from:1
+ to:0
+ duration: FluTheme.enableAnimation ? 83 : 0
+ }
+ }
+ background:Item{
+ FluShadow{
+ radius: 4
+ }
+ }
+ contentItem: Item{
+ clip: true
+ Rectangle{
+ id:container
+ height: 340
+ width: 300
+ radius: 4
+ color: FluTheme.dark ? Qt.rgba(51/255,48/255,48/255,1) : Qt.rgba(248/255,250/255,253/255,1)
+ MouseArea{
+ anchors.fill: parent
+ }
+ RowLayout{
+ id:layout_content
+ spacing: 0
+ width: parent.width
+ height: 300
+ Component{
+ id:list_delegate
+ Item{
+ height:38
+ width:getListView().width
+ function getListView(){
+ if(type === 0)
+ return list_view_1
+ if(type === 1)
+ return list_view_2
+ if(type === 2)
+ return list_view_3
+ }
+ Rectangle{
+ anchors.fill: parent
+ anchors.topMargin: 2
+ anchors.bottomMargin: 2
+ anchors.leftMargin: 5
+ anchors.rightMargin: 5
+ color: {
+ if(getListView().currentIndex === position){
+ return item_mouse.containsMouse ? Qt.darker(FluTheme.primaryColor,1.1) : FluTheme.primaryColor
+ }
+ if(item_mouse.containsMouse){
+ return FluTheme.dark ? Qt.rgba(63/255,60/255,61/255,1) : Qt.rgba(237/255,237/255,242/255,1)
+ }
+ return FluTheme.dark ? Qt.rgba(51/255,48/255,48/255,1) : Qt.rgba(0,0,0,0)
+ }
+ radius: 3
+ MouseArea{
+ id:item_mouse
+ anchors.fill: parent
+ hoverEnabled: true
+ onClicked: {
+ getListView().currentIndex = position
+ if(type === 0){
+ text_hour.text = model
+ }
+ if(type === 1){
+ text_minute.text = model
+ }
+ if(type === 2){
+ text_ampm.text = model
+ }
+ }
+ }
+ FluText{
+ text:model
+ color: {
+ if(getListView().currentIndex === position){
+ if(FluTheme.dark){
+ return Qt.rgba(0,0,0,1)
+ }else{
+ return Qt.rgba(1,1,1,1)
+ }
+ }else{
+ return FluTheme.dark ? "#FFFFFF" : "#1A1A1A"
+ }
+ }
+ anchors.centerIn: parent
+ }
+ }
+ }
+ }
+ ListView{
+ id:list_view_1
+ width: isH ? 100 : 150
+ height: parent.height
+ boundsBehavior:Flickable.StopAtBounds
+ ScrollBar.vertical: FluScrollBar {}
+ preferredHighlightBegin: 0
+ preferredHighlightEnd: 0
+ highlightMoveDuration: 0
+ model: isH ? generateArray(1,12) : generateArray(0,23)
+ clip: true
+ delegate: FluLoader{
+ property var model: modelData
+ property int type:0
+ property int position:index
+ sourceComponent: list_delegate
+ }
+ }
+ Rectangle{
+ width: 1
+ height: parent.height
+ color: dividerColor
+ }
+ ListView{
+ id:list_view_2
+ width: isH ? 100 : 150
+ height: parent.height
+ model: generateArray(0,59)
+ clip: true
+ preferredHighlightBegin: 0
+ preferredHighlightEnd: 0
+ highlightMoveDuration: 0
+ ScrollBar.vertical: FluScrollBar {}
+ boundsBehavior:Flickable.StopAtBounds
+ delegate: FluLoader{
+ property var model: modelData
+ property int type:1
+ property int position:index
+ sourceComponent: list_delegate
+ }
+ }
+ Rectangle{
+ width: 1
+ height: parent.height
+ color: dividerColor
+ visible: isH
+ }
+ ListView{
+ id:list_view_3
+ width: 100
+ height: 76
+ model: [control.amText,control.pmText]
+ clip: true
+ visible: isH
+ preferredHighlightBegin: 0
+ preferredHighlightEnd: 0
+ highlightMoveDuration: 0
+ ScrollBar.vertical: FluScrollBar {}
+ Layout.alignment: Qt.AlignVCenter
+ boundsBehavior:Flickable.StopAtBounds
+ delegate: FluLoader{
+ property var model: modelData
+ property int type:2
+ property int position:index
+ sourceComponent: list_delegate
+ }
+ }
+ }
+ Rectangle{
+ width: parent.width
+ height: 1
+ anchors.top: layout_content.bottom
+ color: dividerColor
+ }
+ Rectangle{
+ id:layout_actions
+ height: 40
+ radius: 5
+ color: FluTheme.dark ? Qt.rgba(32/255,32/255,32/255,1) : Qt.rgba(243/255,243/255,243/255,1)
+ anchors{
+ bottom:parent.bottom
+ left: parent.left
+ right: parent.right
+ }
+ Item {
+ id:divider
+ width: 1
+ height: parent.height
+ anchors.centerIn: parent
+ }
+ FluButton{
+ anchors{
+ left: parent.left
+ leftMargin: 20
+ rightMargin: 10
+ right: divider.left
+ verticalCenter: parent.verticalCenter
+ }
+ text: control.cancelText
+ onClicked: {
+ popup.close()
+ }
+ }
+ FluFilledButton{
+ anchors{
+ right: parent.right
+ left: divider.right
+ rightMargin: 20
+ leftMargin: 10
+ verticalCenter: parent.verticalCenter
+ }
+ text: control.okText
+ onClicked: {
+ d.changeFlag = false
+ popup.close()
+ const hours = text_hour.text
+ const minutes = text_minute.text
+ const period = text_ampm.text
+ const date = new Date()
+ var hours24 = parseInt(hours);
+ if(control.hourFormat === FluTimePickerType.H){
+ if (hours === "12") {
+ hours24 = (period === control.amText) ? 0 : 12;
+ } else {
+ hours24 = (period === control.pmText) ? hours24 : hours24 + 12;
+ }
+ }
+ date.setHours(hours24);
+ date.setMinutes(parseInt(minutes));
+ date.setSeconds(0);
+ current = date
+ control.accepted()
+ }
+ }
+ }
+ }
+ }
+ y:35
+ function showPopup() {
+ d.changeFlag = true
+ d.rowData[0] = text_hour.text
+ d.rowData[1] = text_minute.text
+ d.rowData[2] = text_ampm.text
+ var now = new Date();
+ var hour
+ var ampm;
+ if(isH){
+ hour = now.getHours();
+ if(hour>12){
+ ampm = control.pmText
+ hour = hour-12
+ }else{
+ ampm = control.amText
+ }
+ }else{
+ hour = now.getHours();
+ }
+ hour = text_hour.text === control.hourText ? hour.toString().padStart(2, '0') : text_hour.text
+ var minute = text_minute.text === control.minuteText ? now.getMinutes().toString().padStart(2, '0') : text_minute.text
+ ampm = text_ampm.text === "%1/%2".arg(control.amText).arg(control.pmText) ? ampm : text_ampm.text
+ list_view_1.currentIndex = list_view_1.model.indexOf(hour);
+ list_view_2.currentIndex = list_view_2.model.indexOf(minute);
+ list_view_3.currentIndex = list_view_3.model.indexOf(ampm);
+ text_hour.text = hour
+ text_minute.text = minute
+ if(isH){
+ text_ampm.text = ampm
+ }
+ var pos = control.mapToItem(null, 0, 0)
+ if(d.window.height>pos.y+control.height+container.height){
+ popup.y = control.height
+ } else if(pos.y>container.height){
+ popup.y = -container.height
+ } else {
+ popup.y = d.window.height-(pos.y+container.height)
+ }
+ popup.open()
+ }
+ onClosed: {
+ if(d.changeFlag){
+ text_hour.text = d.rowData[0]
+ text_minute.text = d.rowData[1]
+ text_ampm.text = d.rowData[2]
+ }
+ }
+ }
+ function generateArray(start, n) {
+ var arr = [];
+ for (var i = start; i <= n; i++) {
+ arr.push(i.toString().padStart(2, '0'));
+ }
+ return arr;
+ }
+}
diff --git a/src/FluentUI/Controls/FluTimeline.qml b/src/FluentUI/Controls/FluTimeline.qml
new file mode 100644
index 00000000..4477e00e
--- /dev/null
+++ b/src/FluentUI/Controls/FluTimeline.qml
@@ -0,0 +1,309 @@
+import QtQuick
+import QtQuick.Controls
+import FluentUI
+
+Item{
+ property int mode: FluTimelineType.Left
+ property alias model: repeater.model
+ property color lineColor: FluTheme.dark ? Qt.rgba(80/255,80/255,80/255,1) : Qt.rgba(210/255,210/255,210/255,1)
+ id:control
+ implicitWidth: 380
+ implicitHeight: layout_column.height
+ QtObject{
+ id:d
+ property bool isLeft: control.mode === FluTimelineType.Left
+ property bool isRight: control.mode === FluTimelineType.Right
+ property bool isAlternate: control.mode === FluTimelineType.Alternate
+ property bool hasLable: {
+ if(!model){
+ return false
+ }
+ for(var i=0;icontrol.height)
+ return d.pos.y-height-control.targetMargins - 15
+ return ty
+ }
+ return 0
+ }
+ border.width: 0
+ FluShadow{
+ radius: 5
+ }
+ FluText{
+ text: {
+ if(d.step){
+ return d.step.title
+ }
+ return ""
+ }
+ font: FluTextStyle.BodyStrong
+ elide: Text.ElideRight
+ anchors{
+ top: parent.top
+ left: parent.left
+ topMargin: 15
+ leftMargin: 15
+ right: parent.right
+ rightMargin: 32
+ }
+ }
+ FluText{
+ id: text_desc
+ font: FluTextStyle.Body
+ wrapMode: Text.WrapAnywhere
+ maximumLineCount: 4
+ elide: Text.ElideRight
+ text: {
+ if(d.step){
+ return d.step.description
+ }
+ return ""
+ }
+ anchors{
+ top: parent.top
+ left: parent.left
+ right: parent.right
+ rightMargin: 15
+ topMargin: 42
+ leftMargin: 15
+ }
+ }
+ FluLoader{
+ id: loader_next
+ property bool isEnd: control.index === steps.length-1
+ sourceComponent: com_next_button
+ anchors{
+ top: text_desc.bottom
+ topMargin: 10
+ right: parent.right
+ rightMargin: 15
+ }
+ }
+ FluLoader{
+ id: loader_prev
+ visible: control.index !== 0
+ sourceComponent: com_prev_button
+ anchors{
+ right: loader_next.left
+ top: loader_next.top
+ rightMargin: 14
+ }
+ }
+ FluIconButton{
+ anchors{
+ right: parent.right
+ top: parent.top
+ margins: 10
+ }
+ width: 26
+ height: 26
+ verticalPadding: 0
+ horizontalPadding: 0
+ iconSize: 12
+ iconSource: FluentIcons.ChromeClose
+ onClicked: {
+ control.close()
+ }
+ }
+ }
+ FluIcon{
+ iconSource: layout_panne.dir?FluentIcons.FlickUp:FluentIcons.FlickDown
+ color: layout_panne.color
+ x: {
+ if(d.target){
+ return d.pos.x+d.target.width/2-10
+ }
+ return 0
+ }
+ y: {
+ if(d.target){
+ return d.pos.y+(layout_panne.dir?-height:d.target.height)
+ }
+ return 0
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluTreeView.qml b/src/FluentUI/Controls/FluTreeView.qml
new file mode 100644
index 00000000..12ef0a60
--- /dev/null
+++ b/src/FluentUI/Controls/FluTreeView.qml
@@ -0,0 +1,440 @@
+import QtQuick
+import QtQuick.Window
+import QtQuick.Layouts
+import QtQuick.Controls
+import Qt.labs.qmlmodels
+import FluentUI
+
+Item {
+ property int currentIndex : -1
+ property var dataSource
+ property bool showLine: true
+ property bool draggable: false
+ property int cellHeight: 30
+ property int depthPadding: 30
+ property bool checkable: false
+ property color lineColor: FluTheme.dark ? Qt.rgba(111/255,111/255,111/255,1) : Qt.rgba(217/255,217/255,217/255,1)
+ id:control
+ QtObject {
+ id:d
+ property int dy
+ property var current
+ property int dropIndex: -1
+ property bool isDropTopArea: false
+ property int dragIndex: -1
+ property color hitColor: FluTheme.primaryColor
+ }
+ onDataSourceChanged: {
+ tree_model.setDataSource(dataSource)
+ }
+ FluTreeModel{
+ id:tree_model
+ }
+ ListView{
+ id:table_view
+ ScrollBar.horizontal: FluScrollBar{}
+ ScrollBar.vertical: FluScrollBar{}
+ boundsBehavior: Flickable.StopAtBounds
+ model: tree_model
+ anchors.fill: parent
+ clip: true
+ flickableDirection: Flickable.HorizontalAndVerticalFlick
+ contentWidth: contentItem.childrenRect.width
+ reuseItems: true
+ removeDisplaced : Transition{
+ ParallelAnimation{
+ NumberAnimation {
+ properties: "y"
+ duration: 167
+ from: d.dy + table_view.height
+ easing.type: Easing.OutCubic
+ }
+ NumberAnimation {
+ properties: "opacity"
+ duration: 88
+ from: 0
+ to: 1
+ }
+ }
+ }
+ move: Transition {
+ NumberAnimation { property: "y"; duration: 200 }
+ }
+ add: Transition{
+ ParallelAnimation{
+ NumberAnimation {
+ properties: "y"
+ duration: 167
+ from: d.dy - control.cellHeight
+ easing.type: Easing.OutCubic
+ }
+ NumberAnimation {
+ properties: "opacity"
+ duration: 88
+ from: 0
+ to: 1
+ }
+ }
+ }
+ delegate: Item {
+ id:item_control
+ implicitWidth: item_loader_container.width
+ implicitHeight: item_loader_container.height
+ ListView.onReused: {
+ item_loader_container.item.reused()
+ }
+ ListView.onPooled: {
+ item_loader_container.item.pooled()
+ }
+ FluLoader{
+ property var itemControl: item_control
+ property var itemModel: dataModel
+ property int rowIndex: index
+ property bool isItemLoader: true
+ id:item_loader_container
+ sourceComponent: com_item_container
+ }
+ }
+ FluLoader{
+ id:loader_container
+ property var itemControl
+ property var itemModel
+ property bool isItemLoader: false
+ }
+ }
+ Component{
+ id:com_item_container
+ Item{
+ signal reused
+ signal pooled
+ onReused: {
+
+ }
+ onPooled: {
+ }
+ property bool isCurrent: d.current === itemModel
+ id:item_container
+ width: {
+ var w = 46 + item_loader_cell.width + control.depthPadding*itemModel.depth
+ if(control.width>w){
+ return control.width
+ }
+ return w
+ }
+ height: control.cellHeight
+ implicitWidth: width
+ implicitHeight: height
+ function toggle(){
+ var pos = FluTools.cursorPos()
+ var viewPos = table_view.mapToGlobal(0,0)
+ d.dy = table_view.contentY + pos.y-viewPos.y
+ if(itemModel.isExpanded){
+ tree_model.collapse(rowIndex)
+ }else{
+ tree_model.expand(rowIndex)
+ }
+ }
+ Rectangle{
+ width: 3
+ height: 18
+ radius: 1.5
+ color: FluTheme.primaryColor
+ visible: isCurrent
+ anchors{
+ left: parent.left
+ leftMargin: 6
+ verticalCenter: parent.verticalCenter
+ }
+ }
+ MouseArea{
+ id:item_mouse
+ property point clickPos: Qt.point(0,0)
+ anchors.fill: parent
+ drag.target:control.draggable ? loader_container : undefined
+ hoverEnabled: true
+ drag.onActiveChanged: {
+ if(drag.active){
+ if(itemModel.isExpanded && itemModel.hasChildren()){
+ tree_model.collapse(rowIndex)
+ }
+ d.dragIndex = rowIndex
+ loader_container.sourceComponent = com_item_container
+ }
+ }
+ onPressed:
+ (mouse)=>{
+ clickPos = Qt.point(mouse.x,mouse.y)
+ loader_container.itemControl = itemControl
+ loader_container.itemModel = itemModel
+ var cellPosition = item_container.mapToItem(table_view, 0, 0)
+ loader_container.width = item_container.width
+ loader_container.height = item_container.height
+ loader_container.x = 0
+ loader_container.y = cellPosition.y
+ }
+ onClicked: {
+ d.current = itemModel
+ }
+ onDoubleClicked: {
+ if(itemModel.hasChildren()){
+ item_container.toggle()
+ }
+ }
+ onPositionChanged:
+ (mouse)=> {
+ if(!drag.active){
+ return
+ }
+ var cellPosition = item_container.mapToItem(table_view, 0, 0)
+ if(mouse.y+cellPosition.y<0 || mouse.y+cellPosition.y>table_view.height){
+ d.dropIndex = -1
+ return
+ }
+ if((mouse.x-table_view.contentX)>table_view.width || (mouse.x-table_view.contentX)<0){
+ d.dropIndex = -1
+ return
+ }
+ var pos = FluTools.cursorPos()
+ var viewPos = table_view.mapToGlobal(0,0)
+ var y = table_view.contentY + pos.y-viewPos.y
+ var index = Math.floor(y/control.cellHeight)
+ if(index<0 || index>table_view.count-1){
+ d.dropIndex = -1
+ return
+ }
+ if(tree_model.hitHasChildrenExpanded(index) && y>index*control.cellHeight+control.cellHeight/2){
+ d.dropIndex = index + 1
+ d.isDropTopArea = true
+ }else{
+ d.dropIndex = index
+ if(y>index*control.cellHeight+control.cellHeight/2){
+ d.isDropTopArea = false
+ }else{
+ d.isDropTopArea = true
+ }
+ }
+ }
+ onCanceled: {
+ loader_container.sourceComponent = undefined
+ loader_container.x = 0
+ loader_container.y = 0
+ d.dropIndex = -1
+ d.dragIndex = -1
+ }
+ onReleased: {
+ loader_container.sourceComponent = undefined
+ if(d.dropIndex !== -1){
+ tree_model.dragAnddrop(d.dragIndex,d.dropIndex,d.isDropTopArea)
+ }
+ d.dropIndex = -1
+ d.dragIndex = -1
+ loader_container.x = 0
+ loader_container.y = 0
+ }
+ }
+ Drag.active: item_mouse.drag.active
+ Rectangle{
+ id:item_line_drop_tip
+ anchors{
+ left: layout_row.left
+ leftMargin: 26
+ right: parent.right
+ rightMargin: 10
+ bottom: parent.bottom
+ bottomMargin: -1.5
+ top: undefined
+ }
+ states: [
+ State {
+ when:d.isDropTopArea
+ AnchorChanges {
+ target: item_line_drop_tip
+ anchors.top: item_container.top
+ anchors.bottom: undefined
+ }
+ PropertyChanges {
+ target: item_line_drop_tip
+ anchors.topMargin: -1.5
+ }
+ }
+ ]
+ height: 3
+ radius: 1.5
+ color: d.hitColor
+ visible: d.dropIndex === rowIndex
+ Rectangle{
+ width: 10
+ height: 10
+ radius: 5
+ border.width: 3
+ border.color: d.hitColor
+ color: FluTheme.dark ? FluColors.Black : FluColors.White
+ anchors{
+ top: parent.top
+ left: parent.left
+ topMargin: -3
+ leftMargin: -5
+ }
+ }
+ }
+ FluRectangle{
+ width: 1
+ color: control.lineColor
+ visible: control.showLine && isItemLoader && itemModel.depth !== 0 && !itemModel.hasChildren()
+ height: itemModel.hideLineFooter() ? parent.height/2 : parent.height
+ anchors{
+ top: parent.top
+ left: item_line_h.left
+ }
+ }
+ FluRectangle{
+ id:item_line_h
+ height: 1
+ color: control.lineColor
+ visible: control.showLine && isItemLoader && itemModel.depth !== 0 && !itemModel.hasChildren()
+ width: depthPadding - 10
+ anchors{
+ right: layout_row.left
+ rightMargin: -24
+ verticalCenter: parent.verticalCenter
+ }
+ }
+ Repeater{
+ model: Math.max(itemModel.depth-1,0)
+ delegate: FluRectangle{
+ required property int index
+ width: 1
+ color: control.lineColor
+ visible: control.showLine && isItemLoader && itemModel.depth !== 0 && itemModel.hasNextNodeByIndex(index)
+ anchors{
+ top:parent.top
+ bottom: parent.bottom
+ left: parent.left
+ leftMargin: control.depthPadding*(index+1) + 24
+ }
+ }
+ }
+ Rectangle{
+ anchors.fill: parent
+ radius: 4
+ anchors.leftMargin: 6
+ anchors.rightMargin: 6
+ border.color: d.hitColor
+ border.width: d.dragIndex === rowIndex ? 1 : 0
+ color: {
+ if(isCurrent){
+ return FluTheme.itemCheckColor
+ }
+ if(item_mouse.containsMouse || item_check_box.hovered){
+ return FluTheme.itemHoverColor
+ }
+ if(item_loader_expand.item && item_loader_expand.item.hovered){
+ return FluTheme.itemHoverColor
+ }
+ return FluTheme.itemNormalColor
+ }
+ }
+ RowLayout{
+ id:layout_row
+ height: parent.height
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ spacing: 0
+ anchors.leftMargin: 14 + control.depthPadding*itemModel.depth
+ Component{
+ id:com_icon_btn
+ FluIconButton{
+ opacity: itemModel.hasChildren()
+ onClicked: {
+ item_container.toggle()
+ }
+ contentItem:FluIcon{
+ rotation: itemModel.isExpanded?0:-90
+ iconSource:FluentIcons.ChevronDown
+ iconSize: 16
+ anchors.centerIn: parent
+ }
+ }
+ }
+
+ FluLoader{
+ id:item_loader_expand
+ Layout.preferredWidth: 20
+ Layout.preferredHeight: 20
+ sourceComponent: itemModel.hasChildren() ? com_icon_btn : undefined
+ Layout.alignment: Qt.AlignVCenter
+ }
+
+ FluCheckBox{
+ id:item_check_box
+ Layout.preferredWidth: 18
+ Layout.preferredHeight: 18
+ Layout.leftMargin: 5
+ horizontalPadding:0
+ verticalPadding: 0
+ checked: itemModel.checked
+ enableAnimation:false
+ visible: control.checkable
+ padding: 0
+ clickListener: function(){
+ tree_model.checkRow(rowIndex,!itemModel.checked)
+ }
+ Layout.alignment: Qt.AlignVCenter
+ }
+ FluLoader{
+ property var dataModel: itemModel
+ property var itemMouse: item_mouse
+ id:item_loader_cell
+ Layout.leftMargin: 10
+ Layout.preferredWidth: {
+ if(item){
+ return item.width
+ }
+ return 0
+ }
+ Layout.fillHeight: true
+ sourceComponent:com_item_text
+ }
+ }
+ }
+ }
+ Component{
+ id:com_item_text
+ Item{
+ width: item_text.width
+ FluText {
+ id:item_text
+ text: dataModel.title
+ rightPadding: 14
+ anchors.centerIn: parent
+ color:{
+ if(itemMouse.pressed){
+ return FluTheme.dark ? FluColors.Grey80 : FluColors.Grey120
+ }
+ return FluTheme.dark ? FluColors.White : FluColors.Grey220
+ }
+ }
+ }
+ }
+ function selectionModel(){
+ return tree_model.selectionModel
+ }
+ function count(){
+ return tree_model.dataSourceSize
+ }
+ function visibleCount(){
+ return table_view.count
+ }
+ function collapse(rowIndex){
+ tree_model.collapse(rowIndex)
+ }
+ function expand(rowIndex){
+ tree_model.expand(rowIndex)
+ }
+ function allExpand(){
+ tree_model.allExpand()
+ }
+ function allCollapse(){
+ tree_model.allCollapse()
+ }
+}
diff --git a/src/FluentUI/Controls/FluWindow.qml b/src/FluentUI/Controls/FluWindow.qml
new file mode 100644
index 00000000..8d43c129
--- /dev/null
+++ b/src/FluentUI/Controls/FluWindow.qml
@@ -0,0 +1,320 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import FluentUI
+
+Window {
+ default property alias contentData : layout_content.data
+ property string windowIcon: FluApp.windowIcon
+ property int launchMode: FluWindowType.Standard
+ property var argument:({})
+ property var background : com_background
+ property bool fixSize: false
+ property Component loadingItem: com_loading
+ property bool fitsAppBarWindows: false
+ property Item appBar: FluAppBar {
+ title: window.title
+ height: 30
+ showDark: window.showDark
+ showClose: window.showClose
+ showMinimize: window.showMinimize
+ showMaximize: window.showMaximize
+ showStayTop: window.showStayTop
+ icon: window.windowIcon
+ }
+ property color backgroundColor: {
+ if(active){
+ return FluTheme.windowActiveBackgroundColor
+ }
+ return FluTheme.windowBackgroundColor
+ }
+ property bool stayTop: false
+ property bool showDark: false
+ property bool showClose: true
+ property bool showMinimize: true
+ property bool showMaximize: true
+ property bool showStayTop: false
+ property bool autoMaximize: false
+ property bool autoVisible: true
+ property bool autoCenter: true
+ property bool autoDestory: true
+ property bool useSystemAppBar
+ property color resizeBorderColor: {
+ if(window.active){
+ return FluTheme.dark ? "#333333" : "#6E6E6E"
+ }
+ return FluTheme.dark ? "#3D3D3E" : "#A7A7A7"
+ }
+ property int resizeBorderWidth: 1
+ property var closeListener: function(event){
+ if(autoDestory){
+ destoryOnClose()
+ }else{
+ visible = false
+ event.accepted = false
+ }
+ }
+ signal showSystemMenu
+ signal initArgument(var argument)
+ signal firstVisible()
+ property int _realHeight
+ property int _realWidth
+ property int _appBarHeight: appBar.height
+ property var _windowRegister
+ property string _route
+ id:window
+ color:"transparent"
+ Component.onCompleted: {
+ _realHeight = height
+ _realWidth = width
+ useSystemAppBar = FluApp.useSystemAppBar
+ if(useSystemAppBar && autoCenter){
+ moveWindowToDesktopCenter()
+ }
+ fixWindowSize()
+ lifecycle.onCompleted(window)
+ initArgument(argument)
+ if(!useSystemAppBar){
+ loader_frameless_helper.sourceComponent = com_frameless_helper
+ }
+ if(window.autoVisible){
+ if(window.autoMaximize){
+ window.showMaximized()
+ }else{
+ window.show()
+ }
+ }
+ }
+ Component.onDestruction: {
+ lifecycle.onDestruction()
+ }
+ onShowSystemMenu: {
+ if(loader_frameless_helper.item){
+ loader_frameless_helper.item.showSystemMenu()
+ }
+ }
+ onVisibleChanged: {
+ if(visible && d.isFirstVisible){
+ window.firstVisible()
+ d.isFirstVisible = false
+ }
+ lifecycle.onVisible(visible)
+ }
+ onWidthChanged: {
+ window.appBar.width = width
+ }
+ QtObject{
+ id:d
+ property bool isFirstVisible: true
+ }
+ Connections{
+ target: window
+ function onClosing(event){closeListener(event)}
+ }
+ Component{
+ id:com_frameless_helper
+ FluFramelessHelper{
+ onLoadCompleted:{
+ if(autoCenter){
+ window.moveWindowToDesktopCenter()
+ }
+ }
+ }
+ }
+ Component{
+ id:com_background
+ Rectangle{
+ color: window.backgroundColor
+ }
+ }
+ Component{
+ id:com_app_bar
+ Item{
+ data: window.appBar
+ }
+ }
+ Component{
+ id:com_loading
+ Popup{
+ id:popup_loading
+ focus: true
+ width: window.width
+ height: window.height
+ anchors.centerIn: Overlay.overlay
+ closePolicy: {
+ if(cancel){
+ return Popup.CloseOnEscape | Popup.CloseOnPressOutside
+ }
+ return Popup.NoAutoClose
+ }
+ Overlay.modal: Item {}
+ onVisibleChanged: {
+ if(!visible){
+ loader_loading.sourceComponent = undefined
+ }
+ }
+ padding: 0
+ opacity: 0
+ visible:true
+ Behavior on opacity {
+ SequentialAnimation {
+ PauseAnimation {
+ duration: 88
+ }
+ NumberAnimation{
+ duration: 167
+ }
+ }
+ }
+ Component.onCompleted: {
+ opacity = 1
+ }
+ background: Rectangle{
+ color:"#44000000"
+ }
+ contentItem: Item{
+ MouseArea{
+ anchors.fill: parent
+ onClicked: {
+ if (cancel){
+ popup_loading.visible = false
+ }
+ }
+ }
+ ColumnLayout{
+ spacing: 8
+ anchors.centerIn: parent
+ FluProgressRing{
+ Layout.alignment: Qt.AlignHCenter
+ }
+ FluText{
+ text:loadingText
+ Layout.alignment: Qt.AlignHCenter
+ }
+ }
+ }
+ }
+ }
+ Component{
+ id:com_border
+ Rectangle{
+ color:"transparent"
+ border.width: window.resizeBorderWidth
+ border.color: window.resizeBorderColor
+ }
+ }
+ FluLoader{
+ id:loader_frameless_helper
+ }
+ FluLoader{
+ anchors.fill: parent
+ sourceComponent: background
+ }
+ FluLoader{
+ id:loader_app_bar
+ anchors {
+ top: parent.top
+ left: parent.left
+ right: parent.right
+ }
+ height: {
+ if(window.useSystemAppBar){
+ return 0
+ }
+ return window.fitsAppBarWindows ? 0 : window.appBar.height
+ }
+ sourceComponent: window.useSystemAppBar ? undefined : com_app_bar
+ }
+ Item{
+ id:layout_content
+ anchors{
+ top: loader_app_bar.bottom
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ }
+ clip: true
+ }
+ FluLoader{
+ property string loadingText
+ property bool cancel: false
+ id:loader_loading
+ anchors.fill: parent
+ }
+ FluInfoBar{
+ id:info_bar
+ root: window
+ }
+ FluWindowLifecycle{
+ id:lifecycle
+ }
+ FluLoader{
+ id:loader_border
+ anchors.fill: parent
+ sourceComponent: {
+ if(window.useSystemAppBar){
+ return undefined
+ }
+ if(FluTools.isWindows10OrGreater()){
+ return undefined
+ }
+ if(window.visibility === Window.Maximized || window.visibility === Window.FullScreen){
+ return undefined
+ }
+ return com_border
+ }
+ }
+ function destoryOnClose(){
+ lifecycle.onDestoryOnClose()
+ }
+ function showLoading(text = qsTr("Loading..."),cancel = true){
+ loader_loading.loadingText = text
+ loader_loading.cancel = cancel
+ loader_loading.sourceComponent = com_loading
+ }
+ function hideLoading(){
+ loader_loading.sourceComponent = undefined
+ }
+ function showSuccess(text,duration,moremsg){
+ info_bar.showSuccess(text,duration,moremsg)
+ }
+ function showInfo(text,duration,moremsg){
+ info_bar.showInfo(text,duration,moremsg)
+ }
+ function showWarning(text,duration,moremsg){
+ info_bar.showWarning(text,duration,moremsg)
+ }
+ function showError(text,duration,moremsg){
+ info_bar.showError(text,duration,moremsg)
+ }
+ function moveWindowToDesktopCenter(){
+ screen = Qt.application.screens[FluTools.cursorScreenIndex()]
+ 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)
+ }
+ function fixWindowSize(){
+ if(fixSize){
+ window.maximumWidth = window.width
+ window.maximumHeight = window.height
+ window.minimumWidth = window.width
+ window.minimumHeight = window.height
+ }
+ }
+ function registerForWindowResult(path){
+ return FluApp.createWindowRegister(window,path)
+ }
+ function onResult(data){
+ if(_windowRegister){
+ _windowRegister.onResult(data)
+ }
+ }
+ function showMaximized(){
+ if(FluTools.isWin()){
+ if(loader_frameless_helper.item){
+ loader_frameless_helper.item.showMaximized()
+ }
+ }else{
+ window.visibility = Qt.WindowMaximized
+ }
+ }
+}
diff --git a/src/FluentUI/Controls/FluWindowDialog.qml b/src/FluentUI/Controls/FluWindowDialog.qml
new file mode 100644
index 00000000..60a40d21
--- /dev/null
+++ b/src/FluentUI/Controls/FluWindowDialog.qml
@@ -0,0 +1,33 @@
+import QtQuick
+import QtQuick.Window
+import QtQuick.Controls
+import QtQuick.Layouts
+import FluentUI
+
+FluWindow {
+ id:control
+ property Component contentDelegate
+ autoVisible: false
+ autoCenter: false
+ autoDestory: true
+ fixSize: true
+ Loader{
+ anchors.fill: parent
+ sourceComponent: {
+ if(control.autoDestory){
+ return control.visible ? control.contentDelegate : undefined
+ }
+ return control.contentDelegate
+ }
+ }
+ closeListener: function(event){
+ visible = false
+ event.accepted = false
+ }
+ function showDialog(){
+ var x = transientParent.x + (transientParent.width - width)/2
+ var y = transientParent.y + (transientParent.height - height)/2
+ setGeometry(x,y,width,height)
+ visible = true
+ }
+}
diff --git a/src/FluentUI/Font/Segoe_Fluent_Icons.ttf b/src/FluentUI/Font/Segoe_Fluent_Icons.ttf
new file mode 100644
index 00000000..8f05a4bb
Binary files /dev/null and b/src/FluentUI/Font/Segoe_Fluent_Icons.ttf differ
diff --git a/src/FluentUI/Image/btn_close_hovered.png b/src/FluentUI/Image/btn_close_hovered.png
new file mode 100644
index 00000000..afd646f3
Binary files /dev/null and b/src/FluentUI/Image/btn_close_hovered.png differ
diff --git a/src/FluentUI/Image/btn_close_normal.png b/src/FluentUI/Image/btn_close_normal.png
new file mode 100644
index 00000000..de922d4b
Binary files /dev/null and b/src/FluentUI/Image/btn_close_normal.png differ
diff --git a/src/FluentUI/Image/btn_close_pushed.png b/src/FluentUI/Image/btn_close_pushed.png
new file mode 100644
index 00000000..4c722ba2
Binary files /dev/null and b/src/FluentUI/Image/btn_close_pushed.png differ
diff --git a/src/FluentUI/Image/btn_max_hovered.png b/src/FluentUI/Image/btn_max_hovered.png
new file mode 100644
index 00000000..22ba4ca6
Binary files /dev/null and b/src/FluentUI/Image/btn_max_hovered.png differ
diff --git a/src/FluentUI/Image/btn_max_normal.png b/src/FluentUI/Image/btn_max_normal.png
new file mode 100644
index 00000000..4c1bc629
Binary files /dev/null and b/src/FluentUI/Image/btn_max_normal.png differ
diff --git a/src/FluentUI/Image/btn_max_pushed.png b/src/FluentUI/Image/btn_max_pushed.png
new file mode 100644
index 00000000..22fc333e
Binary files /dev/null and b/src/FluentUI/Image/btn_max_pushed.png differ
diff --git a/src/FluentUI/Image/btn_min_hovered.png b/src/FluentUI/Image/btn_min_hovered.png
new file mode 100644
index 00000000..41e9395e
Binary files /dev/null and b/src/FluentUI/Image/btn_min_hovered.png differ
diff --git a/src/FluentUI/Image/btn_min_normal.png b/src/FluentUI/Image/btn_min_normal.png
new file mode 100644
index 00000000..25fcf5f4
Binary files /dev/null and b/src/FluentUI/Image/btn_min_normal.png differ
diff --git a/src/FluentUI/Image/btn_min_pushed.png b/src/FluentUI/Image/btn_min_pushed.png
new file mode 100644
index 00000000..57f7211a
Binary files /dev/null and b/src/FluentUI/Image/btn_min_pushed.png differ
diff --git a/src/FluentUI/Image/noise.png b/src/FluentUI/Image/noise.png
new file mode 100644
index 00000000..a78e8296
Binary files /dev/null and b/src/FluentUI/Image/noise.png differ
diff --git a/src/FluentUI/JS/Chart.js b/src/FluentUI/JS/Chart.js
new file mode 100644
index 00000000..cce4f915
--- /dev/null
+++ b/src/FluentUI/JS/Chart.js
@@ -0,0 +1,20822 @@
+/*!
+ * Chart.js v2.9.4
+ * https://www.chartjs.org
+ * (c) 2020 Chart.js Contributors
+ * Released under the MIT License
+ */
+
+ /*!
+ * adaptions by Elypson (Michael A. Voelkel) to get it working for QML:
+ * 1) changed overall library structure with UMD and global function build
+ * 2) animations are triggered via QML animator
+ * 3) tooltips do not use DOM events but QML events that are injected via bindEvents now
+ * 4) many smaller modifications because Chart.js relied on and used the DOM that is not available in QML in the same way
+ * 5) some fixes that occured in QML like setting dashed line to solid
+ * 6) personal customization, where we assumed that it leads to better looks
+ *
+ * also note that modifications are inspired by Shuirna (github.com/shuirna) and their changes were inspired by Julien Wintz
+ *
+ * (c) 2020 ChartJs2QML contributors (starting with Elypson, Michael A. Voelkel, https://github.com/Elypson)
+ * those customizations are also licensed under MIT for all matters and purposes
+ */
+
+function UMD(global, factory) {
+typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+typeof define === 'function' && define.amd ? define(factory) :
+(global = global || self, global.Chart = factory());
+};
+
+function Chart (item, config) { 'use strict';
+
+var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof _window !== 'undefined' ? _window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
+
+function commonjsRequire () {
+ throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs');
+}
+
+function createCommonjsModule(fn, module) {
+ return module = { exports: {} }, fn(module, module.exports), module.exports;
+}
+
+function getCjsExportFromNamespace (n) {
+ return n && n['default'] || n;
+}
+
+var colorName = {
+ "aliceblue": [240, 248, 255],
+ "antiquewhite": [250, 235, 215],
+ "aqua": [0, 255, 255],
+ "aquamarine": [127, 255, 212],
+ "azure": [240, 255, 255],
+ "beige": [245, 245, 220],
+ "bisque": [255, 228, 196],
+ "black": [0, 0, 0],
+ "blanchedalmond": [255, 235, 205],
+ "blue": [0, 0, 255],
+ "blueviolet": [138, 43, 226],
+ "brown": [165, 42, 42],
+ "burlywood": [222, 184, 135],
+ "cadetblue": [95, 158, 160],
+ "chartreuse": [127, 255, 0],
+ "chocolate": [210, 105, 30],
+ "coral": [255, 127, 80],
+ "cornflowerblue": [100, 149, 237],
+ "cornsilk": [255, 248, 220],
+ "crimson": [220, 20, 60],
+ "cyan": [0, 255, 255],
+ "darkblue": [0, 0, 139],
+ "darkcyan": [0, 139, 139],
+ "darkgoldenrod": [184, 134, 11],
+ "darkgray": [169, 169, 169],
+ "darkgreen": [0, 100, 0],
+ "darkgrey": [169, 169, 169],
+ "darkkhaki": [189, 183, 107],
+ "darkmagenta": [139, 0, 139],
+ "darkolivegreen": [85, 107, 47],
+ "darkorange": [255, 140, 0],
+ "darkorchid": [153, 50, 204],
+ "darkred": [139, 0, 0],
+ "darksalmon": [233, 150, 122],
+ "darkseagreen": [143, 188, 143],
+ "darkslateblue": [72, 61, 139],
+ "darkslategray": [47, 79, 79],
+ "darkslategrey": [47, 79, 79],
+ "darkturquoise": [0, 206, 209],
+ "darkviolet": [148, 0, 211],
+ "deeppink": [255, 20, 147],
+ "deepskyblue": [0, 191, 255],
+ "dimgray": [105, 105, 105],
+ "dimgrey": [105, 105, 105],
+ "dodgerblue": [30, 144, 255],
+ "firebrick": [178, 34, 34],
+ "floralwhite": [255, 250, 240],
+ "forestgreen": [34, 139, 34],
+ "fuchsia": [255, 0, 255],
+ "gainsboro": [220, 220, 220],
+ "ghostwhite": [248, 248, 255],
+ "gold": [255, 215, 0],
+ "goldenrod": [218, 165, 32],
+ "gray": [128, 128, 128],
+ "green": [0, 128, 0],
+ "greenyellow": [173, 255, 47],
+ "grey": [128, 128, 128],
+ "honeydew": [240, 255, 240],
+ "hotpink": [255, 105, 180],
+ "indianred": [205, 92, 92],
+ "indigo": [75, 0, 130],
+ "ivory": [255, 255, 240],
+ "khaki": [240, 230, 140],
+ "lavender": [230, 230, 250],
+ "lavenderblush": [255, 240, 245],
+ "lawngreen": [124, 252, 0],
+ "lemonchiffon": [255, 250, 205],
+ "lightblue": [173, 216, 230],
+ "lightcoral": [240, 128, 128],
+ "lightcyan": [224, 255, 255],
+ "lightgoldenrodyellow": [250, 250, 210],
+ "lightgray": [211, 211, 211],
+ "lightgreen": [144, 238, 144],
+ "lightgrey": [211, 211, 211],
+ "lightpink": [255, 182, 193],
+ "lightsalmon": [255, 160, 122],
+ "lightseagreen": [32, 178, 170],
+ "lightskyblue": [135, 206, 250],
+ "lightslategray": [119, 136, 153],
+ "lightslategrey": [119, 136, 153],
+ "lightsteelblue": [176, 196, 222],
+ "lightyellow": [255, 255, 224],
+ "lime": [0, 255, 0],
+ "limegreen": [50, 205, 50],
+ "linen": [250, 240, 230],
+ "magenta": [255, 0, 255],
+ "maroon": [128, 0, 0],
+ "mediumaquamarine": [102, 205, 170],
+ "mediumblue": [0, 0, 205],
+ "mediumorchid": [186, 85, 211],
+ "mediumpurple": [147, 112, 219],
+ "mediumseagreen": [60, 179, 113],
+ "mediumslateblue": [123, 104, 238],
+ "mediumspringgreen": [0, 250, 154],
+ "mediumturquoise": [72, 209, 204],
+ "mediumvioletred": [199, 21, 133],
+ "midnightblue": [25, 25, 112],
+ "mintcream": [245, 255, 250],
+ "mistyrose": [255, 228, 225],
+ "moccasin": [255, 228, 181],
+ "navajowhite": [255, 222, 173],
+ "navy": [0, 0, 128],
+ "oldlace": [253, 245, 230],
+ "olive": [128, 128, 0],
+ "olivedrab": [107, 142, 35],
+ "orange": [255, 165, 0],
+ "orangered": [255, 69, 0],
+ "orchid": [218, 112, 214],
+ "palegoldenrod": [238, 232, 170],
+ "palegreen": [152, 251, 152],
+ "paleturquoise": [175, 238, 238],
+ "palevioletred": [219, 112, 147],
+ "papayawhip": [255, 239, 213],
+ "peachpuff": [255, 218, 185],
+ "peru": [205, 133, 63],
+ "pink": [255, 192, 203],
+ "plum": [221, 160, 221],
+ "powderblue": [176, 224, 230],
+ "purple": [128, 0, 128],
+ "rebeccapurple": [102, 51, 153],
+ "red": [255, 0, 0],
+ "rosybrown": [188, 143, 143],
+ "royalblue": [65, 105, 225],
+ "saddlebrown": [139, 69, 19],
+ "salmon": [250, 128, 114],
+ "sandybrown": [244, 164, 96],
+ "seagreen": [46, 139, 87],
+ "seashell": [255, 245, 238],
+ "sienna": [160, 82, 45],
+ "silver": [192, 192, 192],
+ "skyblue": [135, 206, 235],
+ "slateblue": [106, 90, 205],
+ "slategray": [112, 128, 144],
+ "slategrey": [112, 128, 144],
+ "snow": [255, 250, 250],
+ "springgreen": [0, 255, 127],
+ "steelblue": [70, 130, 180],
+ "tan": [210, 180, 140],
+ "teal": [0, 128, 128],
+ "thistle": [216, 191, 216],
+ "tomato": [255, 99, 71],
+ "turquoise": [64, 224, 208],
+ "violet": [238, 130, 238],
+ "wheat": [245, 222, 179],
+ "white": [255, 255, 255],
+ "whitesmoke": [245, 245, 245],
+ "yellow": [255, 255, 0],
+ "yellowgreen": [154, 205, 50]
+};
+
+var conversions = createCommonjsModule(function (module) {
+/* MIT license */
+
+
+// NOTE: conversions should only return primitive values (i.e. arrays, or
+// values that give correct `typeof` results).
+// do not use box values types (i.e. Number(), String(), etc.)
+
+var reverseKeywords = {};
+for (var key in colorName) {
+ if (colorName.hasOwnProperty(key)) {
+ reverseKeywords[colorName[key]] = key;
+ }
+}
+
+var convert = module.exports = {
+ rgb: {channels: 3, labels: 'rgb'},
+ hsl: {channels: 3, labels: 'hsl'},
+ hsv: {channels: 3, labels: 'hsv'},
+ hwb: {channels: 3, labels: 'hwb'},
+ cmyk: {channels: 4, labels: 'cmyk'},
+ xyz: {channels: 3, labels: 'xyz'},
+ lab: {channels: 3, labels: 'lab'},
+ lch: {channels: 3, labels: 'lch'},
+ hex: {channels: 1, labels: ['hex']},
+ keyword: {channels: 1, labels: ['keyword']},
+ ansi16: {channels: 1, labels: ['ansi16']},
+ ansi256: {channels: 1, labels: ['ansi256']},
+ hcg: {channels: 3, labels: ['h', 'c', 'g']},
+ apple: {channels: 3, labels: ['r16', 'g16', 'b16']},
+ gray: {channels: 1, labels: ['gray']}
+};
+
+// hide .channels and .labels properties
+for (var model in convert) {
+ if (convert.hasOwnProperty(model)) {
+ if (!('channels' in convert[model])) {
+ throw new Error('missing channels property: ' + model);
+ }
+
+ if (!('labels' in convert[model])) {
+ throw new Error('missing channel labels property: ' + model);
+ }
+
+ if (convert[model].labels.length !== convert[model].channels) {
+ throw new Error('channel and label counts mismatch: ' + model);
+ }
+
+ var channels = convert[model].channels;
+ var labels = convert[model].labels;
+ delete convert[model].channels;
+ delete convert[model].labels;
+ Object.defineProperty(convert[model], 'channels', {value: channels});
+ Object.defineProperty(convert[model], 'labels', {value: labels});
+ }
+}
+
+convert.rgb.hsl = function (rgb) {
+ var r = rgb[0] / 255;
+ var g = rgb[1] / 255;
+ var b = rgb[2] / 255;
+ var min = Math.min(r, g, b);
+ var max = Math.max(r, g, b);
+ var delta = max - min;
+ var h;
+ var s;
+ var l;
+
+ if (max === min) {
+ h = 0;
+ } else if (r === max) {
+ h = (g - b) / delta;
+ } else if (g === max) {
+ h = 2 + (b - r) / delta;
+ } else if (b === max) {
+ h = 4 + (r - g) / delta;
+ }
+
+ h = Math.min(h * 60, 360);
+
+ if (h < 0) {
+ h += 360;
+ }
+
+ l = (min + max) / 2;
+
+ if (max === min) {
+ s = 0;
+ } else if (l <= 0.5) {
+ s = delta / (max + min);
+ } else {
+ s = delta / (2 - max - min);
+ }
+
+ return [h, s * 100, l * 100];
+};
+
+convert.rgb.hsv = function (rgb) {
+ var rdif;
+ var gdif;
+ var bdif;
+ var h;
+ var s;
+
+ var r = rgb[0] / 255;
+ var g = rgb[1] / 255;
+ var b = rgb[2] / 255;
+ var v = Math.max(r, g, b);
+ var diff = v - Math.min(r, g, b);
+ var diffc = function (c) {
+ return (v - c) / 6 / diff + 1 / 2;
+ };
+
+ if (diff === 0) {
+ h = s = 0;
+ } else {
+ s = diff / v;
+ rdif = diffc(r);
+ gdif = diffc(g);
+ bdif = diffc(b);
+
+ if (r === v) {
+ h = bdif - gdif;
+ } else if (g === v) {
+ h = (1 / 3) + rdif - bdif;
+ } else if (b === v) {
+ h = (2 / 3) + gdif - rdif;
+ }
+ if (h < 0) {
+ h += 1;
+ } else if (h > 1) {
+ h -= 1;
+ }
+ }
+
+ return [
+ h * 360,
+ s * 100,
+ v * 100
+ ];
+};
+
+convert.rgb.hwb = function (rgb) {
+ var r = rgb[0];
+ var g = rgb[1];
+ var b = rgb[2];
+ var h = convert.rgb.hsl(rgb)[0];
+ var w = 1 / 255 * Math.min(r, Math.min(g, b));
+
+ b = 1 - 1 / 255 * Math.max(r, Math.max(g, b));
+
+ return [h, w * 100, b * 100];
+};
+
+convert.rgb.cmyk = function (rgb) {
+ var r = rgb[0] / 255;
+ var g = rgb[1] / 255;
+ var b = rgb[2] / 255;
+ var c;
+ var m;
+ var y;
+ var k;
+
+ k = Math.min(1 - r, 1 - g, 1 - b);
+ c = (1 - r - k) / (1 - k) || 0;
+ m = (1 - g - k) / (1 - k) || 0;
+ y = (1 - b - k) / (1 - k) || 0;
+
+ return [c * 100, m * 100, y * 100, k * 100];
+};
+
+/**
+ * See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance
+ * */
+function comparativeDistance(x, y) {
+ return (
+ Math.pow(x[0] - y[0], 2) +
+ Math.pow(x[1] - y[1], 2) +
+ Math.pow(x[2] - y[2], 2)
+ );
+}
+
+convert.rgb.keyword = function (rgb) {
+ var reversed = reverseKeywords[rgb];
+ if (reversed) {
+ return reversed;
+ }
+
+ var currentClosestDistance = Infinity;
+ var currentClosestKeyword;
+
+ for (var keyword in colorName) {
+ if (colorName.hasOwnProperty(keyword)) {
+ var value = colorName[keyword];
+
+ // Compute comparative distance
+ var distance = comparativeDistance(rgb, value);
+
+ // Check if its less, if so set as closest
+ if (distance < currentClosestDistance) {
+ currentClosestDistance = distance;
+ currentClosestKeyword = keyword;
+ }
+ }
+ }
+
+ return currentClosestKeyword;
+};
+
+convert.keyword.rgb = function (keyword) {
+ return colorName[keyword];
+};
+
+convert.rgb.xyz = function (rgb) {
+ var r = rgb[0] / 255;
+ var g = rgb[1] / 255;
+ var b = rgb[2] / 255;
+
+ // assume sRGB
+ r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);
+ g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);
+ b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);
+
+ var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
+ var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
+ var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);
+
+ return [x * 100, y * 100, z * 100];
+};
+
+convert.rgb.lab = function (rgb) {
+ var xyz = convert.rgb.xyz(rgb);
+ var x = xyz[0];
+ var y = xyz[1];
+ var z = xyz[2];
+ var l;
+ var a;
+ var b;
+
+ x /= 95.047;
+ y /= 100;
+ z /= 108.883;
+
+ x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116);
+ y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116);
+ z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116);
+
+ l = (116 * y) - 16;
+ a = 500 * (x - y);
+ b = 200 * (y - z);
+
+ return [l, a, b];
+};
+
+convert.hsl.rgb = function (hsl) {
+ var h = hsl[0] / 360;
+ var s = hsl[1] / 100;
+ var l = hsl[2] / 100;
+ var t1;
+ var t2;
+ var t3;
+ var rgb;
+ var val;
+
+ if (s === 0) {
+ val = l * 255;
+ return [val, val, val];
+ }
+
+ if (l < 0.5) {
+ t2 = l * (1 + s);
+ } else {
+ t2 = l + s - l * s;
+ }
+
+ t1 = 2 * l - t2;
+
+ rgb = [0, 0, 0];
+ for (var i = 0; i < 3; i++) {
+ t3 = h + 1 / 3 * -(i - 1);
+ if (t3 < 0) {
+ t3++;
+ }
+ if (t3 > 1) {
+ t3--;
+ }
+
+ if (6 * t3 < 1) {
+ val = t1 + (t2 - t1) * 6 * t3;
+ } else if (2 * t3 < 1) {
+ val = t2;
+ } else if (3 * t3 < 2) {
+ val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
+ } else {
+ val = t1;
+ }
+
+ rgb[i] = val * 255;
+ }
+
+ return rgb;
+};
+
+convert.hsl.hsv = function (hsl) {
+ var h = hsl[0];
+ var s = hsl[1] / 100;
+ var l = hsl[2] / 100;
+ var smin = s;
+ var lmin = Math.max(l, 0.01);
+ var sv;
+ var v;
+
+ l *= 2;
+ s *= (l <= 1) ? l : 2 - l;
+ smin *= lmin <= 1 ? lmin : 2 - lmin;
+ v = (l + s) / 2;
+ sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s);
+
+ return [h, sv * 100, v * 100];
+};
+
+convert.hsv.rgb = function (hsv) {
+ var h = hsv[0] / 60;
+ var s = hsv[1] / 100;
+ var v = hsv[2] / 100;
+ var hi = Math.floor(h) % 6;
+
+ var f = h - Math.floor(h);
+ var p = 255 * v * (1 - s);
+ var q = 255 * v * (1 - (s * f));
+ var t = 255 * v * (1 - (s * (1 - f)));
+ v *= 255;
+
+ switch (hi) {
+ case 0:
+ return [v, t, p];
+ case 1:
+ return [q, v, p];
+ case 2:
+ return [p, v, t];
+ case 3:
+ return [p, q, v];
+ case 4:
+ return [t, p, v];
+ case 5:
+ return [v, p, q];
+ }
+};
+
+convert.hsv.hsl = function (hsv) {
+ var h = hsv[0];
+ var s = hsv[1] / 100;
+ var v = hsv[2] / 100;
+ var vmin = Math.max(v, 0.01);
+ var lmin;
+ var sl;
+ var l;
+
+ l = (2 - s) * v;
+ lmin = (2 - s) * vmin;
+ sl = s * vmin;
+ sl /= (lmin <= 1) ? lmin : 2 - lmin;
+ sl = sl || 0;
+ l /= 2;
+
+ return [h, sl * 100, l * 100];
+};
+
+// http://dev.w3.org/csswg/css-color/#hwb-to-rgb
+convert.hwb.rgb = function (hwb) {
+ var h = hwb[0] / 360;
+ var wh = hwb[1] / 100;
+ var bl = hwb[2] / 100;
+ var ratio = wh + bl;
+ var i;
+ var v;
+ var f;
+ var n;
+
+ // wh + bl cant be > 1
+ if (ratio > 1) {
+ wh /= ratio;
+ bl /= ratio;
+ }
+
+ i = Math.floor(6 * h);
+ v = 1 - bl;
+ f = 6 * h - i;
+
+ if ((i & 0x01) !== 0) {
+ f = 1 - f;
+ }
+
+ n = wh + f * (v - wh); // linear interpolation
+
+ var r;
+ var g;
+ var b;
+ switch (i) {
+ default:
+ case 6:
+ case 0: r = v; g = n; b = wh; break;
+ case 1: r = n; g = v; b = wh; break;
+ case 2: r = wh; g = v; b = n; break;
+ case 3: r = wh; g = n; b = v; break;
+ case 4: r = n; g = wh; b = v; break;
+ case 5: r = v; g = wh; b = n; break;
+ }
+
+ return [r * 255, g * 255, b * 255];
+};
+
+convert.cmyk.rgb = function (cmyk) {
+ var c = cmyk[0] / 100;
+ var m = cmyk[1] / 100;
+ var y = cmyk[2] / 100;
+ var k = cmyk[3] / 100;
+ var r;
+ var g;
+ var b;
+
+ r = 1 - Math.min(1, c * (1 - k) + k);
+ g = 1 - Math.min(1, m * (1 - k) + k);
+ b = 1 - Math.min(1, y * (1 - k) + k);
+
+ return [r * 255, g * 255, b * 255];
+};
+
+convert.xyz.rgb = function (xyz) {
+ var x = xyz[0] / 100;
+ var y = xyz[1] / 100;
+ var z = xyz[2] / 100;
+ var r;
+ var g;
+ var b;
+
+ r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
+ g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
+ b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);
+
+ // assume sRGB
+ r = r > 0.0031308
+ ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
+ : r * 12.92;
+
+ g = g > 0.0031308
+ ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
+ : g * 12.92;
+
+ b = b > 0.0031308
+ ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
+ : b * 12.92;
+
+ r = Math.min(Math.max(0, r), 1);
+ g = Math.min(Math.max(0, g), 1);
+ b = Math.min(Math.max(0, b), 1);
+
+ return [r * 255, g * 255, b * 255];
+};
+
+convert.xyz.lab = function (xyz) {
+ var x = xyz[0];
+ var y = xyz[1];
+ var z = xyz[2];
+ var l;
+ var a;
+ var b;
+
+ x /= 95.047;
+ y /= 100;
+ z /= 108.883;
+
+ x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116);
+ y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116);
+ z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116);
+
+ l = (116 * y) - 16;
+ a = 500 * (x - y);
+ b = 200 * (y - z);
+
+ return [l, a, b];
+};
+
+convert.lab.xyz = function (lab) {
+ var l = lab[0];
+ var a = lab[1];
+ var b = lab[2];
+ var x;
+ var y;
+ var z;
+
+ y = (l + 16) / 116;
+ x = a / 500 + y;
+ z = y - b / 200;
+
+ var y2 = Math.pow(y, 3);
+ var x2 = Math.pow(x, 3);
+ var z2 = Math.pow(z, 3);
+ y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787;
+ x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787;
+ z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787;
+
+ x *= 95.047;
+ y *= 100;
+ z *= 108.883;
+
+ return [x, y, z];
+};
+
+convert.lab.lch = function (lab) {
+ var l = lab[0];
+ var a = lab[1];
+ var b = lab[2];
+ var hr;
+ var h;
+ var c;
+
+ hr = Math.atan2(b, a);
+ h = hr * 360 / 2 / Math.PI;
+
+ if (h < 0) {
+ h += 360;
+ }
+
+ c = Math.sqrt(a * a + b * b);
+
+ return [l, c, h];
+};
+
+convert.lch.lab = function (lch) {
+ var l = lch[0];
+ var c = lch[1];
+ var h = lch[2];
+ var a;
+ var b;
+ var hr;
+
+ hr = h / 360 * 2 * Math.PI;
+ a = c * Math.cos(hr);
+ b = c * Math.sin(hr);
+
+ return [l, a, b];
+};
+
+convert.rgb.ansi16 = function (args) {
+ var r = args[0];
+ var g = args[1];
+ var b = args[2];
+ var value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization
+
+ value = Math.round(value / 50);
+
+ if (value === 0) {
+ return 30;
+ }
+
+ var ansi = 30
+ + ((Math.round(b / 255) << 2)
+ | (Math.round(g / 255) << 1)
+ | Math.round(r / 255));
+
+ if (value === 2) {
+ ansi += 60;
+ }
+
+ return ansi;
+};
+
+convert.hsv.ansi16 = function (args) {
+ // optimization here; we already know the value and don't need to get
+ // it converted for us.
+ return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]);
+};
+
+convert.rgb.ansi256 = function (args) {
+ var r = args[0];
+ var g = args[1];
+ var b = args[2];
+
+ // we use the extended greyscale palette here, with the exception of
+ // black and white. normal palette only has 4 greyscale shades.
+ if (r === g && g === b) {
+ if (r < 8) {
+ return 16;
+ }
+
+ if (r > 248) {
+ return 231;
+ }
+
+ return Math.round(((r - 8) / 247) * 24) + 232;
+ }
+
+ var ansi = 16
+ + (36 * Math.round(r / 255 * 5))
+ + (6 * Math.round(g / 255 * 5))
+ + Math.round(b / 255 * 5);
+
+ return ansi;
+};
+
+convert.ansi16.rgb = function (args) {
+ var color = args % 10;
+
+ // handle greyscale
+ if (color === 0 || color === 7) {
+ if (args > 50) {
+ color += 3.5;
+ }
+
+ color = color / 10.5 * 255;
+
+ return [color, color, color];
+ }
+
+ var mult = (~~(args > 50) + 1) * 0.5;
+ var r = ((color & 1) * mult) * 255;
+ var g = (((color >> 1) & 1) * mult) * 255;
+ var b = (((color >> 2) & 1) * mult) * 255;
+
+ return [r, g, b];
+};
+
+convert.ansi256.rgb = function (args) {
+ // handle greyscale
+ if (args >= 232) {
+ var c = (args - 232) * 10 + 8;
+ return [c, c, c];
+ }
+
+ args -= 16;
+
+ var rem;
+ var r = Math.floor(args / 36) / 5 * 255;
+ var g = Math.floor((rem = args % 36) / 6) / 5 * 255;
+ var b = (rem % 6) / 5 * 255;
+
+ return [r, g, b];
+};
+
+convert.rgb.hex = function (args) {
+ var integer = ((Math.round(args[0]) & 0xFF) << 16)
+ + ((Math.round(args[1]) & 0xFF) << 8)
+ + (Math.round(args[2]) & 0xFF);
+
+ var string = integer.toString(16).toUpperCase();
+ return '000000'.substring(string.length) + string;
+};
+
+convert.hex.rgb = function (args) {
+ var match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);
+ if (!match) {
+ return [0, 0, 0];
+ }
+
+ var colorString = match[0];
+
+ if (match[0].length === 3) {
+ colorString = colorString.split('').map(function (char) {
+ return char + char;
+ }).join('');
+ }
+
+ var integer = parseInt(colorString, 16);
+ var r = (integer >> 16) & 0xFF;
+ var g = (integer >> 8) & 0xFF;
+ var b = integer & 0xFF;
+
+ return [r, g, b];
+};
+
+convert.rgb.hcg = function (rgb) {
+ var r = rgb[0] / 255;
+ var g = rgb[1] / 255;
+ var b = rgb[2] / 255;
+ var max = Math.max(Math.max(r, g), b);
+ var min = Math.min(Math.min(r, g), b);
+ var chroma = (max - min);
+ var grayscale;
+ var hue;
+
+ if (chroma < 1) {
+ grayscale = min / (1 - chroma);
+ } else {
+ grayscale = 0;
+ }
+
+ if (chroma <= 0) {
+ hue = 0;
+ } else
+ if (max === r) {
+ hue = ((g - b) / chroma) % 6;
+ } else
+ if (max === g) {
+ hue = 2 + (b - r) / chroma;
+ } else {
+ hue = 4 + (r - g) / chroma + 4;
+ }
+
+ hue /= 6;
+ hue %= 1;
+
+ return [hue * 360, chroma * 100, grayscale * 100];
+};
+
+convert.hsl.hcg = function (hsl) {
+ var s = hsl[1] / 100;
+ var l = hsl[2] / 100;
+ var c = 1;
+ var f = 0;
+
+ if (l < 0.5) {
+ c = 2.0 * s * l;
+ } else {
+ c = 2.0 * s * (1.0 - l);
+ }
+
+ if (c < 1.0) {
+ f = (l - 0.5 * c) / (1.0 - c);
+ }
+
+ return [hsl[0], c * 100, f * 100];
+};
+
+convert.hsv.hcg = function (hsv) {
+ var s = hsv[1] / 100;
+ var v = hsv[2] / 100;
+
+ var c = s * v;
+ var f = 0;
+
+ if (c < 1.0) {
+ f = (v - c) / (1 - c);
+ }
+
+ return [hsv[0], c * 100, f * 100];
+};
+
+convert.hcg.rgb = function (hcg) {
+ var h = hcg[0] / 360;
+ var c = hcg[1] / 100;
+ var g = hcg[2] / 100;
+
+ if (c === 0.0) {
+ return [g * 255, g * 255, g * 255];
+ }
+
+ var pure = [0, 0, 0];
+ var hi = (h % 1) * 6;
+ var v = hi % 1;
+ var w = 1 - v;
+ var mg = 0;
+
+ switch (Math.floor(hi)) {
+ case 0:
+ pure[0] = 1; pure[1] = v; pure[2] = 0; break;
+ case 1:
+ pure[0] = w; pure[1] = 1; pure[2] = 0; break;
+ case 2:
+ pure[0] = 0; pure[1] = 1; pure[2] = v; break;
+ case 3:
+ pure[0] = 0; pure[1] = w; pure[2] = 1; break;
+ case 4:
+ pure[0] = v; pure[1] = 0; pure[2] = 1; break;
+ default:
+ pure[0] = 1; pure[1] = 0; pure[2] = w;
+ }
+
+ mg = (1.0 - c) * g;
+
+ return [
+ (c * pure[0] + mg) * 255,
+ (c * pure[1] + mg) * 255,
+ (c * pure[2] + mg) * 255
+ ];
+};
+
+convert.hcg.hsv = function (hcg) {
+ var c = hcg[1] / 100;
+ var g = hcg[2] / 100;
+
+ var v = c + g * (1.0 - c);
+ var f = 0;
+
+ if (v > 0.0) {
+ f = c / v;
+ }
+
+ return [hcg[0], f * 100, v * 100];
+};
+
+convert.hcg.hsl = function (hcg) {
+ var c = hcg[1] / 100;
+ var g = hcg[2] / 100;
+
+ var l = g * (1.0 - c) + 0.5 * c;
+ var s = 0;
+
+ if (l > 0.0 && l < 0.5) {
+ s = c / (2 * l);
+ } else
+ if (l >= 0.5 && l < 1.0) {
+ s = c / (2 * (1 - l));
+ }
+
+ return [hcg[0], s * 100, l * 100];
+};
+
+convert.hcg.hwb = function (hcg) {
+ var c = hcg[1] / 100;
+ var g = hcg[2] / 100;
+ var v = c + g * (1.0 - c);
+ return [hcg[0], (v - c) * 100, (1 - v) * 100];
+};
+
+convert.hwb.hcg = function (hwb) {
+ var w = hwb[1] / 100;
+ var b = hwb[2] / 100;
+ var v = 1 - b;
+ var c = v - w;
+ var g = 0;
+
+ if (c < 1) {
+ g = (v - c) / (1 - c);
+ }
+
+ return [hwb[0], c * 100, g * 100];
+};
+
+convert.apple.rgb = function (apple) {
+ return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255];
+};
+
+convert.rgb.apple = function (rgb) {
+ return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535];
+};
+
+convert.gray.rgb = function (args) {
+ return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255];
+};
+
+convert.gray.hsl = convert.gray.hsv = function (args) {
+ return [0, 0, args[0]];
+};
+
+convert.gray.hwb = function (gray) {
+ return [0, 100, gray[0]];
+};
+
+convert.gray.cmyk = function (gray) {
+ return [0, 0, 0, gray[0]];
+};
+
+convert.gray.lab = function (gray) {
+ return [gray[0], 0, 0];
+};
+
+convert.gray.hex = function (gray) {
+ var val = Math.round(gray[0] / 100 * 255) & 0xFF;
+ var integer = (val << 16) + (val << 8) + val;
+
+ var string = integer.toString(16).toUpperCase();
+ return '000000'.substring(string.length) + string;
+};
+
+convert.rgb.gray = function (rgb) {
+ var val = (rgb[0] + rgb[1] + rgb[2]) / 3;
+ return [val / 255 * 100];
+};
+});
+var conversions_1 = conversions.rgb;
+var conversions_2 = conversions.hsl;
+var conversions_3 = conversions.hsv;
+var conversions_4 = conversions.hwb;
+var conversions_5 = conversions.cmyk;
+var conversions_6 = conversions.xyz;
+var conversions_7 = conversions.lab;
+var conversions_8 = conversions.lch;
+var conversions_9 = conversions.hex;
+var conversions_10 = conversions.keyword;
+var conversions_11 = conversions.ansi16;
+var conversions_12 = conversions.ansi256;
+var conversions_13 = conversions.hcg;
+var conversions_14 = conversions.apple;
+var conversions_15 = conversions.gray;
+
+/*
+ this function routes a model to all other models.
+
+ all functions that are routed have a property `.conversion` attached
+ to the returned synthetic function. This property is an array
+ of strings, each with the steps in between the 'from' and 'to'
+ color models (inclusive).
+
+ conversions that are not possible simply are not included.
+*/
+
+function buildGraph() {
+ var graph = {};
+ // https://jsperf.com/object-keys-vs-for-in-with-closure/3
+ var models = Object.keys(conversions);
+
+ for (var len = models.length, i = 0; i < len; i++) {
+ graph[models[i]] = {
+ // http://jsperf.com/1-vs-infinity
+ // micro-opt, but this is simple.
+ distance: -1,
+ parent: null
+ };
+ }
+
+ return graph;
+}
+
+// https://en.wikipedia.org/wiki/Breadth-first_search
+function deriveBFS(fromModel) {
+ var graph = buildGraph();
+ var queue = [fromModel]; // unshift -> queue -> pop
+
+ graph[fromModel].distance = 0;
+
+ while (queue.length) {
+ var current = queue.pop();
+ var adjacents = Object.keys(conversions[current]);
+
+ for (var len = adjacents.length, i = 0; i < len; i++) {
+ var adjacent = adjacents[i];
+ var node = graph[adjacent];
+
+ if (node.distance === -1) {
+ node.distance = graph[current].distance + 1;
+ node.parent = current;
+ queue.unshift(adjacent);
+ }
+ }
+ }
+
+ return graph;
+}
+
+function link(from, to) {
+ return function (args) {
+ return to(from(args));
+ };
+}
+
+function wrapConversion(toModel, graph) {
+ var path = [graph[toModel].parent, toModel];
+ var fn = conversions[graph[toModel].parent][toModel];
+
+ var cur = graph[toModel].parent;
+ while (graph[cur].parent) {
+ path.unshift(graph[cur].parent);
+ fn = link(conversions[graph[cur].parent][cur], fn);
+ cur = graph[cur].parent;
+ }
+
+ fn.conversion = path;
+ return fn;
+}
+
+var route = function (fromModel) {
+ var graph = deriveBFS(fromModel);
+ var conversion = {};
+
+ var models = Object.keys(graph);
+ for (var len = models.length, i = 0; i < len; i++) {
+ var toModel = models[i];
+ var node = graph[toModel];
+
+ if (node.parent === null) {
+ // no possible conversion, or this node is the source model.
+ continue;
+ }
+
+ conversion[toModel] = wrapConversion(toModel, graph);
+ }
+
+ return conversion;
+};
+
+var convert = {};
+
+var models = Object.keys(conversions);
+
+function wrapRaw(fn) {
+ var wrappedFn = function (args) {
+ if (args === undefined || args === null) {
+ return args;
+ }
+
+ if (arguments.length > 1) {
+ args = Array.prototype.slice.call(arguments);
+ }
+
+ return fn(args);
+ };
+
+ // preserve .conversion property if there is one
+ if ('conversion' in fn) {
+ wrappedFn.conversion = fn.conversion;
+ }
+
+ return wrappedFn;
+}
+
+function wrapRounded(fn) {
+ var wrappedFn = function (args) {
+ if (args === undefined || args === null) {
+ return args;
+ }
+
+ if (arguments.length > 1) {
+ args = Array.prototype.slice.call(arguments);
+ }
+
+ var result = fn(args);
+
+ // we're assuming the result is an array here.
+ // see notice in conversions.js; don't use box types
+ // in conversion functions.
+ if (typeof result === 'object') {
+ for (var len = result.length, i = 0; i < len; i++) {
+ result[i] = Math.round(result[i]);
+ }
+ }
+
+ return result;
+ };
+
+ // preserve .conversion property if there is one
+ if ('conversion' in fn) {
+ wrappedFn.conversion = fn.conversion;
+ }
+
+ return wrappedFn;
+}
+
+models.forEach(function (fromModel) {
+ convert[fromModel] = {};
+
+ Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels});
+ Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels});
+
+ var routes = route(fromModel);
+ var routeModels = Object.keys(routes);
+
+ routeModels.forEach(function (toModel) {
+ var fn = routes[toModel];
+
+ convert[fromModel][toModel] = wrapRounded(fn);
+ convert[fromModel][toModel].raw = wrapRaw(fn);
+ });
+});
+
+var colorConvert = convert;
+
+var colorName$1 = {
+ "aliceblue": [240, 248, 255],
+ "antiquewhite": [250, 235, 215],
+ "aqua": [0, 255, 255],
+ "aquamarine": [127, 255, 212],
+ "azure": [240, 255, 255],
+ "beige": [245, 245, 220],
+ "bisque": [255, 228, 196],
+ "black": [0, 0, 0],
+ "blanchedalmond": [255, 235, 205],
+ "blue": [0, 0, 255],
+ "blueviolet": [138, 43, 226],
+ "brown": [165, 42, 42],
+ "burlywood": [222, 184, 135],
+ "cadetblue": [95, 158, 160],
+ "chartreuse": [127, 255, 0],
+ "chocolate": [210, 105, 30],
+ "coral": [255, 127, 80],
+ "cornflowerblue": [100, 149, 237],
+ "cornsilk": [255, 248, 220],
+ "crimson": [220, 20, 60],
+ "cyan": [0, 255, 255],
+ "darkblue": [0, 0, 139],
+ "darkcyan": [0, 139, 139],
+ "darkgoldenrod": [184, 134, 11],
+ "darkgray": [169, 169, 169],
+ "darkgreen": [0, 100, 0],
+ "darkgrey": [169, 169, 169],
+ "darkkhaki": [189, 183, 107],
+ "darkmagenta": [139, 0, 139],
+ "darkolivegreen": [85, 107, 47],
+ "darkorange": [255, 140, 0],
+ "darkorchid": [153, 50, 204],
+ "darkred": [139, 0, 0],
+ "darksalmon": [233, 150, 122],
+ "darkseagreen": [143, 188, 143],
+ "darkslateblue": [72, 61, 139],
+ "darkslategray": [47, 79, 79],
+ "darkslategrey": [47, 79, 79],
+ "darkturquoise": [0, 206, 209],
+ "darkviolet": [148, 0, 211],
+ "deeppink": [255, 20, 147],
+ "deepskyblue": [0, 191, 255],
+ "dimgray": [105, 105, 105],
+ "dimgrey": [105, 105, 105],
+ "dodgerblue": [30, 144, 255],
+ "firebrick": [178, 34, 34],
+ "floralwhite": [255, 250, 240],
+ "forestgreen": [34, 139, 34],
+ "fuchsia": [255, 0, 255],
+ "gainsboro": [220, 220, 220],
+ "ghostwhite": [248, 248, 255],
+ "gold": [255, 215, 0],
+ "goldenrod": [218, 165, 32],
+ "gray": [128, 128, 128],
+ "green": [0, 128, 0],
+ "greenyellow": [173, 255, 47],
+ "grey": [128, 128, 128],
+ "honeydew": [240, 255, 240],
+ "hotpink": [255, 105, 180],
+ "indianred": [205, 92, 92],
+ "indigo": [75, 0, 130],
+ "ivory": [255, 255, 240],
+ "khaki": [240, 230, 140],
+ "lavender": [230, 230, 250],
+ "lavenderblush": [255, 240, 245],
+ "lawngreen": [124, 252, 0],
+ "lemonchiffon": [255, 250, 205],
+ "lightblue": [173, 216, 230],
+ "lightcoral": [240, 128, 128],
+ "lightcyan": [224, 255, 255],
+ "lightgoldenrodyellow": [250, 250, 210],
+ "lightgray": [211, 211, 211],
+ "lightgreen": [144, 238, 144],
+ "lightgrey": [211, 211, 211],
+ "lightpink": [255, 182, 193],
+ "lightsalmon": [255, 160, 122],
+ "lightseagreen": [32, 178, 170],
+ "lightskyblue": [135, 206, 250],
+ "lightslategray": [119, 136, 153],
+ "lightslategrey": [119, 136, 153],
+ "lightsteelblue": [176, 196, 222],
+ "lightyellow": [255, 255, 224],
+ "lime": [0, 255, 0],
+ "limegreen": [50, 205, 50],
+ "linen": [250, 240, 230],
+ "magenta": [255, 0, 255],
+ "maroon": [128, 0, 0],
+ "mediumaquamarine": [102, 205, 170],
+ "mediumblue": [0, 0, 205],
+ "mediumorchid": [186, 85, 211],
+ "mediumpurple": [147, 112, 219],
+ "mediumseagreen": [60, 179, 113],
+ "mediumslateblue": [123, 104, 238],
+ "mediumspringgreen": [0, 250, 154],
+ "mediumturquoise": [72, 209, 204],
+ "mediumvioletred": [199, 21, 133],
+ "midnightblue": [25, 25, 112],
+ "mintcream": [245, 255, 250],
+ "mistyrose": [255, 228, 225],
+ "moccasin": [255, 228, 181],
+ "navajowhite": [255, 222, 173],
+ "navy": [0, 0, 128],
+ "oldlace": [253, 245, 230],
+ "olive": [128, 128, 0],
+ "olivedrab": [107, 142, 35],
+ "orange": [255, 165, 0],
+ "orangered": [255, 69, 0],
+ "orchid": [218, 112, 214],
+ "palegoldenrod": [238, 232, 170],
+ "palegreen": [152, 251, 152],
+ "paleturquoise": [175, 238, 238],
+ "palevioletred": [219, 112, 147],
+ "papayawhip": [255, 239, 213],
+ "peachpuff": [255, 218, 185],
+ "peru": [205, 133, 63],
+ "pink": [255, 192, 203],
+ "plum": [221, 160, 221],
+ "powderblue": [176, 224, 230],
+ "purple": [128, 0, 128],
+ "rebeccapurple": [102, 51, 153],
+ "red": [255, 0, 0],
+ "rosybrown": [188, 143, 143],
+ "royalblue": [65, 105, 225],
+ "saddlebrown": [139, 69, 19],
+ "salmon": [250, 128, 114],
+ "sandybrown": [244, 164, 96],
+ "seagreen": [46, 139, 87],
+ "seashell": [255, 245, 238],
+ "sienna": [160, 82, 45],
+ "silver": [192, 192, 192],
+ "skyblue": [135, 206, 235],
+ "slateblue": [106, 90, 205],
+ "slategray": [112, 128, 144],
+ "slategrey": [112, 128, 144],
+ "snow": [255, 250, 250],
+ "springgreen": [0, 255, 127],
+ "steelblue": [70, 130, 180],
+ "tan": [210, 180, 140],
+ "teal": [0, 128, 128],
+ "thistle": [216, 191, 216],
+ "tomato": [255, 99, 71],
+ "turquoise": [64, 224, 208],
+ "violet": [238, 130, 238],
+ "wheat": [245, 222, 179],
+ "white": [255, 255, 255],
+ "whitesmoke": [245, 245, 245],
+ "yellow": [255, 255, 0],
+ "yellowgreen": [154, 205, 50]
+};
+
+/* MIT license */
+
+
+var colorString = {
+ getRgba: getRgba,
+ getHsla: getHsla,
+ getRgb: getRgb,
+ getHsl: getHsl,
+ getHwb: getHwb,
+ getAlpha: getAlpha,
+
+ hexString: hexString,
+ rgbString: rgbString,
+ rgbaString: rgbaString,
+ percentString: percentString,
+ percentaString: percentaString,
+ hslString: hslString,
+ hslaString: hslaString,
+ hwbString: hwbString,
+ keyword: keyword
+};
+
+function getRgba(string) {
+ if (!string) {
+ return;
+ }
+ var abbr = /^#([a-fA-F0-9]{3,4})$/i,
+ hex = /^#([a-fA-F0-9]{6}([a-fA-F0-9]{2})?)$/i,
+ rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i,
+ per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i,
+ keyword = /(\w+)/;
+
+ var rgb = [0, 0, 0],
+ a = 1,
+ match = string.match(abbr),
+ hexAlpha = "";
+ if (match) {
+ match = match[1];
+ hexAlpha = match[3];
+ for (var i = 0; i < rgb.length; i++) {
+ rgb[i] = parseInt(match[i] + match[i], 16);
+ }
+ if (hexAlpha) {
+ a = Math.round((parseInt(hexAlpha + hexAlpha, 16) / 255) * 100) / 100;
+ }
+ }
+ else if (match = string.match(hex)) {
+ hexAlpha = match[2];
+ match = match[1];
+ for (var i = 0; i < rgb.length; i++) {
+ rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16);
+ }
+ if (hexAlpha) {
+ a = Math.round((parseInt(hexAlpha, 16) / 255) * 100) / 100;
+ }
+ }
+ else if (match = string.match(rgba)) {
+ for (var i = 0; i < rgb.length; i++) {
+ rgb[i] = parseInt(match[i + 1]);
+ }
+ a = parseFloat(match[4]);
+ }
+ else if (match = string.match(per)) {
+ for (var i = 0; i < rgb.length; i++) {
+ rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);
+ }
+ a = parseFloat(match[4]);
+ }
+ else if (match = string.match(keyword)) {
+ if (match[1] == "transparent") {
+ return [0, 0, 0, 0];
+ }
+ rgb = colorName$1[match[1]];
+ if (!rgb) {
+ return;
+ }
+ }
+
+ for (var i = 0; i < rgb.length; i++) {
+ rgb[i] = scale(rgb[i], 0, 255);
+ }
+ if (!a && a != 0) {
+ a = 1;
+ }
+ else {
+ a = scale(a, 0, 1);
+ }
+ rgb[3] = a;
+ return rgb;
+}
+
+function getHsla(string) {
+ if (!string) {
+ return;
+ }
+ var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
+ var match = string.match(hsl);
+ if (match) {
+ var alpha = parseFloat(match[4]);
+ var h = scale(parseInt(match[1]), 0, 360),
+ s = scale(parseFloat(match[2]), 0, 100),
+ l = scale(parseFloat(match[3]), 0, 100),
+ a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
+ return [h, s, l, a];
+ }
+}
+
+function getHwb(string) {
+ if (!string) {
+ return;
+ }
+ var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
+ var match = string.match(hwb);
+ if (match) {
+ var alpha = parseFloat(match[4]);
+ var h = scale(parseInt(match[1]), 0, 360),
+ w = scale(parseFloat(match[2]), 0, 100),
+ b = scale(parseFloat(match[3]), 0, 100),
+ a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
+ return [h, w, b, a];
+ }
+}
+
+function getRgb(string) {
+ var rgba = getRgba(string);
+ return rgba && rgba.slice(0, 3);
+}
+
+function getHsl(string) {
+ var hsla = getHsla(string);
+ return hsla && hsla.slice(0, 3);
+}
+
+function getAlpha(string) {
+ var vals = getRgba(string);
+ if (vals) {
+ return vals[3];
+ }
+ else if (vals = getHsla(string)) {
+ return vals[3];
+ }
+ else if (vals = getHwb(string)) {
+ return vals[3];
+ }
+}
+
+// generators
+function hexString(rgba, a) {
+ var a = (a !== undefined && rgba.length === 3) ? a : rgba[3];
+ return "#" + hexDouble(rgba[0])
+ + hexDouble(rgba[1])
+ + hexDouble(rgba[2])
+ + (
+ (a >= 0 && a < 1)
+ ? hexDouble(Math.round(a * 255))
+ : ""
+ );
+}
+
+function rgbString(rgba, alpha) {
+ if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
+ return rgbaString(rgba, alpha);
+ }
+ return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")";
+}
+
+function rgbaString(rgba, alpha) {
+ if (alpha === undefined) {
+ alpha = (rgba[3] !== undefined ? rgba[3] : 1);
+ }
+ return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2]
+ + ", " + alpha + ")";
+}
+
+function percentString(rgba, alpha) {
+ if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
+ return percentaString(rgba, alpha);
+ }
+ var r = Math.round(rgba[0]/255 * 100),
+ g = Math.round(rgba[1]/255 * 100),
+ b = Math.round(rgba[2]/255 * 100);
+
+ return "rgb(" + r + "%, " + g + "%, " + b + "%)";
+}
+
+function percentaString(rgba, alpha) {
+ var r = Math.round(rgba[0]/255 * 100),
+ g = Math.round(rgba[1]/255 * 100),
+ b = Math.round(rgba[2]/255 * 100);
+ return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")";
+}
+
+function hslString(hsla, alpha) {
+ if (alpha < 1 || (hsla[3] && hsla[3] < 1)) {
+ return hslaString(hsla, alpha);
+ }
+ return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)";
+}
+
+function hslaString(hsla, alpha) {
+ if (alpha === undefined) {
+ alpha = (hsla[3] !== undefined ? hsla[3] : 1);
+ }
+ return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, "
+ + alpha + ")";
+}
+
+// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax
+// (hwb have alpha optional & 1 is default value)
+function hwbString(hwb, alpha) {
+ if (alpha === undefined) {
+ alpha = (hwb[3] !== undefined ? hwb[3] : 1);
+ }
+ return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%"
+ + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")";
+}
+
+// helpers
+function scale(num, min, max) {
+ return Math.min(Math.max(min, num), max);
+}
+
+function hexDouble(num) {
+ var str = num.toString(16).toUpperCase();
+ return (str.length < 2) ? "0" + str : str;
+}
+
+//create a list of reverse color names
+var reverseNames = {};
+for (var name in colorName$1) {
+ reverseNames[colorName$1[name]] = name;
+}
+
+function keyword(rgb) {
+ return reverseNames[rgb.slice(0, 3)];
+}
+
+/* MIT license */
+
+
+
+var Color = function (obj) {
+ if (obj instanceof Color) {
+ return obj;
+ }
+ if (!(this instanceof Color)) {
+ return new Color(obj);
+ }
+
+ this.valid = false;
+ this.values = {
+ rgb: [0, 0, 0],
+ hsl: [0, 0, 0],
+ hsv: [0, 0, 0],
+ hwb: [0, 0, 0],
+ cmyk: [0, 0, 0, 0],
+ alpha: 1
+ };
+
+ // parse Color() argument
+ var vals;
+ if (typeof obj === 'string') {
+ vals = colorString.getRgba(obj);
+ if (vals) {
+ this.setValues('rgb', vals);
+ } else if (vals = colorString.getHsla(obj)) {
+ this.setValues('hsl', vals);
+ } else if (vals = colorString.getHwb(obj)) {
+ this.setValues('hwb', vals);
+ }
+ } else if (typeof obj === 'object') {
+ vals = obj;
+ if (vals.r !== undefined || vals.red !== undefined) {
+ this.setValues('rgb', vals);
+ } else if (vals.l !== undefined || vals.lightness !== undefined) {
+ this.setValues('hsl', vals);
+ } else if (vals.v !== undefined || vals.value !== undefined) {
+ this.setValues('hsv', vals);
+ } else if (vals.w !== undefined || vals.whiteness !== undefined) {
+ this.setValues('hwb', vals);
+ } else if (vals.c !== undefined || vals.cyan !== undefined) {
+ this.setValues('cmyk', vals);
+ }
+ }
+};
+
+Color.prototype = {
+ isValid: function () {
+ return this.valid;
+ },
+ rgb: function () {
+ return this.setSpace('rgb', arguments);
+ },
+ hsl: function () {
+ return this.setSpace('hsl', arguments);
+ },
+ hsv: function () {
+ return this.setSpace('hsv', arguments);
+ },
+ hwb: function () {
+ return this.setSpace('hwb', arguments);
+ },
+ cmyk: function () {
+ return this.setSpace('cmyk', arguments);
+ },
+
+ rgbArray: function () {
+ return this.values.rgb;
+ },
+ hslArray: function () {
+ return this.values.hsl;
+ },
+ hsvArray: function () {
+ return this.values.hsv;
+ },
+ hwbArray: function () {
+ var values = this.values;
+ if (values.alpha !== 1) {
+ return values.hwb.concat([values.alpha]);
+ }
+ return values.hwb;
+ },
+ cmykArray: function () {
+ return this.values.cmyk;
+ },
+ rgbaArray: function () {
+ var values = this.values;
+ return values.rgb.concat([values.alpha]);
+ },
+ hslaArray: function () {
+ var values = this.values;
+ return values.hsl.concat([values.alpha]);
+ },
+ alpha: function (val) {
+ if (val === undefined) {
+ return this.values.alpha;
+ }
+ this.setValues('alpha', val);
+ return this;
+ },
+
+ red: function (val) {
+ return this.setChannel('rgb', 0, val);
+ },
+ green: function (val) {
+ return this.setChannel('rgb', 1, val);
+ },
+ blue: function (val) {
+ return this.setChannel('rgb', 2, val);
+ },
+ hue: function (val) {
+ if (val) {
+ val %= 360;
+ val = val < 0 ? 360 + val : val;
+ }
+ return this.setChannel('hsl', 0, val);
+ },
+ saturation: function (val) {
+ return this.setChannel('hsl', 1, val);
+ },
+ lightness: function (val) {
+ return this.setChannel('hsl', 2, val);
+ },
+ saturationv: function (val) {
+ return this.setChannel('hsv', 1, val);
+ },
+ whiteness: function (val) {
+ return this.setChannel('hwb', 1, val);
+ },
+ blackness: function (val) {
+ return this.setChannel('hwb', 2, val);
+ },
+ value: function (val) {
+ return this.setChannel('hsv', 2, val);
+ },
+ cyan: function (val) {
+ return this.setChannel('cmyk', 0, val);
+ },
+ magenta: function (val) {
+ return this.setChannel('cmyk', 1, val);
+ },
+ yellow: function (val) {
+ return this.setChannel('cmyk', 2, val);
+ },
+ black: function (val) {
+ return this.setChannel('cmyk', 3, val);
+ },
+
+ hexString: function () {
+ return colorString.hexString(this.values.rgb);
+ },
+ rgbString: function () {
+ return colorString.rgbString(this.values.rgb, this.values.alpha);
+ },
+ rgbaString: function () {
+ return colorString.rgbaString(this.values.rgb, this.values.alpha);
+ },
+ percentString: function () {
+ return colorString.percentString(this.values.rgb, this.values.alpha);
+ },
+ hslString: function () {
+ return colorString.hslString(this.values.hsl, this.values.alpha);
+ },
+ hslaString: function () {
+ return colorString.hslaString(this.values.hsl, this.values.alpha);
+ },
+ hwbString: function () {
+ return colorString.hwbString(this.values.hwb, this.values.alpha);
+ },
+ keyword: function () {
+ return colorString.keyword(this.values.rgb, this.values.alpha);
+ },
+
+ rgbNumber: function () {
+ var rgb = this.values.rgb;
+ return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
+ },
+
+ luminosity: function () {
+ // http://www.w3.org/TR/WCAG20/#relativeluminancedef
+ var rgb = this.values.rgb;
+ var lum = [];
+ for (var i = 0; i < rgb.length; i++) {
+ var chan = rgb[i] / 255;
+ lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4);
+ }
+ return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
+ },
+
+ contrast: function (color2) {
+ // http://www.w3.org/TR/WCAG20/#contrast-ratiodef
+ var lum1 = this.luminosity();
+ var lum2 = color2.luminosity();
+ if (lum1 > lum2) {
+ return (lum1 + 0.05) / (lum2 + 0.05);
+ }
+ return (lum2 + 0.05) / (lum1 + 0.05);
+ },
+
+ level: function (color2) {
+ var contrastRatio = this.contrast(color2);
+ if (contrastRatio >= 7.1) {
+ return 'AAA';
+ }
+
+ return (contrastRatio >= 4.5) ? 'AA' : '';
+ },
+
+ dark: function () {
+ // YIQ equation from http://24ways.org/2010/calculating-color-contrast
+ var rgb = this.values.rgb;
+ var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
+ return yiq < 128;
+ },
+
+ light: function () {
+ return !this.dark();
+ },
+
+ negate: function () {
+ var rgb = [];
+ for (var i = 0; i < 3; i++) {
+ rgb[i] = 255 - this.values.rgb[i];
+ }
+ this.setValues('rgb', rgb);
+ return this;
+ },
+
+ lighten: function (ratio) {
+ var hsl = this.values.hsl;
+ hsl[2] += hsl[2] * ratio;
+ this.setValues('hsl', hsl);
+ return this;
+ },
+
+ darken: function (ratio) {
+ var hsl = this.values.hsl;
+ hsl[2] -= hsl[2] * ratio;
+ this.setValues('hsl', hsl);
+ return this;
+ },
+
+ saturate: function (ratio) {
+ var hsl = this.values.hsl;
+ hsl[1] += hsl[1] * ratio;
+ this.setValues('hsl', hsl);
+ return this;
+ },
+
+ desaturate: function (ratio) {
+ var hsl = this.values.hsl;
+ hsl[1] -= hsl[1] * ratio;
+ this.setValues('hsl', hsl);
+ return this;
+ },
+
+ whiten: function (ratio) {
+ var hwb = this.values.hwb;
+ hwb[1] += hwb[1] * ratio;
+ this.setValues('hwb', hwb);
+ return this;
+ },
+
+ blacken: function (ratio) {
+ var hwb = this.values.hwb;
+ hwb[2] += hwb[2] * ratio;
+ this.setValues('hwb', hwb);
+ return this;
+ },
+
+ greyscale: function () {
+ var rgb = this.values.rgb;
+ // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
+ var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
+ this.setValues('rgb', [val, val, val]);
+ return this;
+ },
+
+ clearer: function (ratio) {
+ var alpha = this.values.alpha;
+ this.setValues('alpha', alpha - (alpha * ratio));
+ return this;
+ },
+
+ opaquer: function (ratio) {
+ var alpha = this.values.alpha;
+ this.setValues('alpha', alpha + (alpha * ratio));
+ return this;
+ },
+
+ rotate: function (degrees) {
+ var hsl = this.values.hsl;
+ var hue = (hsl[0] + degrees) % 360;
+ hsl[0] = hue < 0 ? 360 + hue : hue;
+ this.setValues('hsl', hsl);
+ return this;
+ },
+
+ /**
+ * Ported from sass implementation in C
+ * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209
+ */
+ mix: function (mixinColor, weight) {
+ var color1 = this;
+ var color2 = mixinColor;
+ var p = weight === undefined ? 0.5 : weight;
+
+ var w = 2 * p - 1;
+ var a = color1.alpha() - color2.alpha();
+
+ var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
+ var w2 = 1 - w1;
+
+ return this
+ .rgb(
+ w1 * color1.red() + w2 * color2.red(),
+ w1 * color1.green() + w2 * color2.green(),
+ w1 * color1.blue() + w2 * color2.blue()
+ )
+ .alpha(color1.alpha() * p + color2.alpha() * (1 - p));
+ },
+
+ toJSON: function () {
+ return this.rgb();
+ },
+
+ clone: function () {
+ // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify,
+ // making the final build way to big to embed in Chart.js. So let's do it manually,
+ // assuming that values to clone are 1 dimension arrays containing only numbers,
+ // except 'alpha' which is a number.
+ var result = new Color();
+ var source = this.values;
+ var target = result.values;
+ var value, type;
+
+ for (var prop in source) {
+ if (source.hasOwnProperty(prop)) {
+ value = source[prop];
+ type = ({}).toString.call(value);
+ if (type === '[object Array]') {
+ target[prop] = value.slice(0);
+ } else if (type === '[object Number]') {
+ target[prop] = value;
+ } else {
+ console.error('unexpected color value:', value);
+ }
+ }
+ }
+
+ return result;
+ }
+};
+
+Color.prototype.spaces = {
+ rgb: ['red', 'green', 'blue'],
+ hsl: ['hue', 'saturation', 'lightness'],
+ hsv: ['hue', 'saturation', 'value'],
+ hwb: ['hue', 'whiteness', 'blackness'],
+ cmyk: ['cyan', 'magenta', 'yellow', 'black']
+};
+
+Color.prototype.maxes = {
+ rgb: [255, 255, 255],
+ hsl: [360, 100, 100],
+ hsv: [360, 100, 100],
+ hwb: [360, 100, 100],
+ cmyk: [100, 100, 100, 100]
+};
+
+Color.prototype.getValues = function (space) {
+ var values = this.values;
+ var vals = {};
+
+ for (var i = 0; i < space.length; i++) {
+ vals[space.charAt(i)] = values[space][i];
+ }
+
+ if (values.alpha !== 1) {
+ vals.a = values.alpha;
+ }
+
+ // {r: 255, g: 255, b: 255, a: 0.4}
+ return vals;
+};
+
+Color.prototype.setValues = function (space, vals) {
+ var values = this.values;
+ var spaces = this.spaces;
+ var maxes = this.maxes;
+ var alpha = 1;
+ var i;
+
+ this.valid = true;
+
+ if (space === 'alpha') {
+ alpha = vals;
+ } else if (vals.length) {
+ // [10, 10, 10]
+ values[space] = vals.slice(0, space.length);
+ alpha = vals[space.length];
+ } else if (vals[space.charAt(0)] !== undefined) {
+ // {r: 10, g: 10, b: 10}
+ for (i = 0; i < space.length; i++) {
+ values[space][i] = vals[space.charAt(i)];
+ }
+
+ alpha = vals.a;
+ } else if (vals[spaces[space][0]] !== undefined) {
+ // {red: 10, green: 10, blue: 10}
+ var chans = spaces[space];
+
+ for (i = 0; i < space.length; i++) {
+ values[space][i] = vals[chans[i]];
+ }
+
+ alpha = vals.alpha;
+ }
+
+ values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha)));
+
+ if (space === 'alpha') {
+ return false;
+ }
+
+ var capped;
+
+ // cap values of the space prior converting all values
+ for (i = 0; i < space.length; i++) {
+ capped = Math.max(0, Math.min(maxes[space][i], values[space][i]));
+ values[space][i] = Math.round(capped);
+ }
+
+ // convert to all the other color spaces
+ for (var sname in spaces) {
+ if (sname !== space) {
+ values[sname] = colorConvert[space][sname](values[space]);
+ }
+ }
+
+ return true;
+};
+
+Color.prototype.setSpace = function (space, args) {
+ var vals = args[0];
+
+ if (vals === undefined) {
+ // color.rgb()
+ return this.getValues(space);
+ }
+
+ // color.rgb(10, 10, 10)
+ if (typeof vals === 'number') {
+ vals = Array.prototype.slice.call(args);
+ }
+
+ this.setValues(space, vals);
+ return this;
+};
+
+Color.prototype.setChannel = function (space, index, val) {
+ var svalues = this.values[space];
+ if (val === undefined) {
+ // color.red()
+ return svalues[index];
+ } else if (val === svalues[index]) {
+ // color.red(color.red())
+ return this;
+ }
+
+ // color.red(100)
+ svalues[index] = val;
+ this.setValues(space, svalues);
+
+ return this;
+};
+
+if (typeof _window !== 'undefined') {
+ console.debug(_window)
+ _window.Color = Color;
+}
+
+var chartjsColor = Color;
+
+function isValidKey(key) {
+ return ['__proto__', 'prototype', 'constructor'].indexOf(key) === -1;
+}
+
+/**
+ * @namespace Chart.helpers
+ */
+var helpers = {
+ /**
+ * An empty function that can be used, for example, for optional callback.
+ */
+ noop: function() {},
+
+ /**
+ * Returns a unique id, sequentially generated from a global variable.
+ * @returns {number}
+ * @function
+ */
+ uid: (function() {
+ var id = 0;
+ return function() {
+ return id++;
+ };
+ }()),
+
+ /**
+ * Returns true if `value` is neither null nor undefined, else returns false.
+ * @param {*} value - The value to test.
+ * @returns {boolean}
+ * @since 2.7.0
+ */
+ isNullOrUndef: function(value) {
+ return value === null || typeof value === 'undefined';
+ },
+
+ /**
+ * Returns true if `value` is an array (including typed arrays), else returns false.
+ * @param {*} value - The value to test.
+ * @returns {boolean}
+ * @function
+ */
+ isArray: function(value) {
+ if (Array.isArray && Array.isArray(value)) {
+ return true;
+ }
+ var type = Object.prototype.toString.call(value);
+ if (type.substr(0, 7) === '[object' && type.substr(-6) === 'Array]') {
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Returns true if `value` is an object (excluding null), else returns false.
+ * @param {*} value - The value to test.
+ * @returns {boolean}
+ * @since 2.7.0
+ */
+ isObject: function(value) {
+ return value !== null && Object.prototype.toString.call(value) === '[object Object]';
+ },
+
+ /**
+ * Returns true if `value` is a finite number, else returns false
+ * @param {*} value - The value to test.
+ * @returns {boolean}
+ */
+ isFinite: function(value) {
+ return (typeof value === 'number' || value instanceof Number) && isFinite(value);
+ },
+
+ /**
+ * Returns `value` if defined, else returns `defaultValue`.
+ * @param {*} value - The value to return if defined.
+ * @param {*} defaultValue - The value to return if `value` is undefined.
+ * @returns {*}
+ */
+ valueOrDefault: function(value, defaultValue) {
+ return typeof value === 'undefined' ? defaultValue : value;
+ },
+
+ /**
+ * Returns value at the given `index` in array if defined, else returns `defaultValue`.
+ * @param {Array} value - The array to lookup for value at `index`.
+ * @param {number} index - The index in `value` to lookup for value.
+ * @param {*} defaultValue - The value to return if `value[index]` is undefined.
+ * @returns {*}
+ */
+ valueAtIndexOrDefault: function(value, index, defaultValue) {
+ return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue);
+ },
+
+ /**
+ * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the
+ * value returned by `fn`. If `fn` is not a function, this method returns undefined.
+ * @param {function} fn - The function to call.
+ * @param {Array|undefined|null} args - The arguments with which `fn` should be called.
+ * @param {object} [thisArg] - The value of `this` provided for the call to `fn`.
+ * @returns {*}
+ */
+ callback: function(fn, args, thisArg) {
+ if (fn && typeof fn.call === 'function') {
+ return fn.apply(thisArg, args);
+ }
+ },
+
+ /**
+ * Note(SB) for performance sake, this method should only be used when loopable type
+ * is unknown or in none intensive code (not called often and small loopable). Else
+ * it's preferable to use a regular for() loop and save extra function calls.
+ * @param {object|Array} loopable - The object or array to be iterated.
+ * @param {function} fn - The function to call for each item.
+ * @param {object} [thisArg] - The value of `this` provided for the call to `fn`.
+ * @param {boolean} [reverse] - If true, iterates backward on the loopable.
+ */
+ each: function(loopable, fn, thisArg, reverse) {
+ var i, len, keys;
+ if (helpers.isArray(loopable)) {
+ len = loopable.length;
+ if (reverse) {
+ for (i = len - 1; i >= 0; i--) {
+ fn.call(thisArg, loopable[i], i);
+ }
+ } else {
+ for (i = 0; i < len; i++) {
+ fn.call(thisArg, loopable[i], i);
+ }
+ }
+ } else if (helpers.isObject(loopable)) {
+ keys = Object.keys(loopable);
+ len = keys.length;
+ for (i = 0; i < len; i++) {
+ fn.call(thisArg, loopable[keys[i]], keys[i]);
+ }
+ }
+ },
+
+ /**
+ * Returns true if the `a0` and `a1` arrays have the same content, else returns false.
+ * @see https://stackoverflow.com/a/14853974
+ * @param {Array} a0 - The array to compare
+ * @param {Array} a1 - The array to compare
+ * @returns {boolean}
+ */
+ arrayEquals: function(a0, a1) {
+ var i, ilen, v0, v1;
+
+ if (!a0 || !a1 || a0.length !== a1.length) {
+ return false;
+ }
+
+ for (i = 0, ilen = a0.length; i < ilen; ++i) {
+ v0 = a0[i];
+ v1 = a1[i];
+
+ if (v0 instanceof Array && v1 instanceof Array) {
+ if (!helpers.arrayEquals(v0, v1)) {
+ return false;
+ }
+ } else if (v0 !== v1) {
+ // NOTE: two different object instances will never be equal: {x:20} != {x:20}
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ /**
+ * Returns a deep copy of `source` without keeping references on objects and arrays.
+ * @param {*} source - The value to clone.
+ * @returns {*}
+ */
+ clone: function(source) {
+ if (helpers.isArray(source)) {
+ return source.map(helpers.clone);
+ }
+
+ if (helpers.isObject(source)) {
+ var target = Object.create(source);
+ var keys = Object.keys(source);
+ var klen = keys.length;
+ var k = 0;
+
+ for (; k < klen; ++k) {
+ target[keys[k]] = helpers.clone(source[keys[k]]);
+ }
+
+ return target;
+ }
+
+ return source;
+ },
+
+ /**
+ * The default merger when Chart.helpers.merge is called without merger option.
+ * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback.
+ * @private
+ */
+ _merger: function(key, target, source, options) {
+ if (!isValidKey(key)) {
+ // We want to ensure we do not copy prototypes over
+ // as this can pollute global namespaces
+ return;
+ }
+
+ var tval = target[key];
+ var sval = source[key];
+
+ if (helpers.isObject(tval) && helpers.isObject(sval)) {
+ helpers.merge(tval, sval, options);
+ } else {
+ target[key] = helpers.clone(sval);
+ }
+ },
+
+ /**
+ * Merges source[key] in target[key] only if target[key] is undefined.
+ * @private
+ */
+ _mergerIf: function(key, target, source) {
+ if (!isValidKey(key)) {
+ // We want to ensure we do not copy prototypes over
+ // as this can pollute global namespaces
+ return;
+ }
+
+ var tval = target[key];
+ var sval = source[key];
+
+ if (helpers.isObject(tval) && helpers.isObject(sval)) {
+ helpers.mergeIf(tval, sval);
+ } else if (!target.hasOwnProperty(key)) {
+ target[key] = helpers.clone(sval);
+ }
+ },
+
+ /**
+ * Recursively deep copies `source` properties into `target` with the given `options`.
+ * IMPORTANT: `target` is not cloned and will be updated with `source` properties.
+ * @param {object} target - The target object in which all sources are merged into.
+ * @param {object|object[]} source - Object(s) to merge into `target`.
+ * @param {object} [options] - Merging options:
+ * @param {function} [options.merger] - The merge method (key, target, source, options)
+ * @returns {object} The `target` object.
+ */
+ merge: function(target, source, options) {
+ var sources = helpers.isArray(source) ? source : [source];
+ var ilen = sources.length;
+ var merge, i, keys, klen, k;
+
+ if (!helpers.isObject(target)) {
+ return target;
+ }
+
+ options = options || {};
+ merge = options.merger || helpers._merger;
+
+ for (i = 0; i < ilen; ++i) {
+ source = sources[i];
+ if (!helpers.isObject(source)) {
+ continue;
+ }
+
+ keys = Object.keys(source);
+ for (k = 0, klen = keys.length; k < klen; ++k) {
+ merge(keys[k], target, source, options);
+ }
+ }
+
+ return target;
+ },
+
+ /**
+ * Recursively deep copies `source` properties into `target` *only* if not defined in target.
+ * IMPORTANT: `target` is not cloned and will be updated with `source` properties.
+ * @param {object} target - The target object in which all sources are merged into.
+ * @param {object|object[]} source - Object(s) to merge into `target`.
+ * @returns {object} The `target` object.
+ */
+ mergeIf: function(target, source) {
+ return helpers.merge(target, source, {merger: helpers._mergerIf});
+ },
+
+ /**
+ * Applies the contents of two or more objects together into the first object.
+ * @param {object} target - The target object in which all objects are merged into.
+ * @param {object} arg1 - Object containing additional properties to merge in target.
+ * @param {object} argN - Additional objects containing properties to merge in target.
+ * @returns {object} The `target` object.
+ */
+ extend: Object.assign || function(target) {
+ return helpers.merge(target, [].slice.call(arguments, 1), {
+ merger: function(key, dst, src) {
+ dst[key] = src[key];
+ }
+ });
+ },
+
+ /**
+ * Basic javascript inheritance based on the model created in Backbone.js
+ */
+ inherits: function(extensions) {
+ var me = this;
+ var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() {
+ return me.apply(this, arguments);
+ };
+
+ var Surrogate = function() {
+ this.constructor = ChartElement;
+ };
+
+ Surrogate.prototype = me.prototype;
+ ChartElement.prototype = new Surrogate();
+ ChartElement.extend = helpers.inherits;
+
+ if (extensions) {
+ helpers.extend(ChartElement.prototype, extensions);
+ }
+
+ ChartElement.__super__ = me.prototype;
+ return ChartElement;
+ },
+
+ _deprecated: function(scope, value, previous, current) {
+ if (value !== undefined) {
+ console.warn(scope + ': "' + previous +
+ '" is deprecated. Please use "' + current + '" instead');
+ }
+ }
+};
+
+var helpers_core = helpers;
+
+// DEPRECATIONS
+
+/**
+ * Provided for backward compatibility, use Chart.helpers.callback instead.
+ * @function Chart.helpers.callCallback
+ * @deprecated since version 2.6.0
+ * @todo remove at version 3
+ * @private
+ */
+helpers.callCallback = helpers.callback;
+
+/**
+ * Provided for backward compatibility, use Array.prototype.indexOf instead.
+ * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+
+ * @function Chart.helpers.indexOf
+ * @deprecated since version 2.7.0
+ * @todo remove at version 3
+ * @private
+ */
+helpers.indexOf = function(array, item, fromIndex) {
+ return Array.prototype.indexOf.call(array, item, fromIndex);
+};
+
+/**
+ * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead.
+ * @function Chart.helpers.getValueOrDefault
+ * @deprecated since version 2.7.0
+ * @todo remove at version 3
+ * @private
+ */
+helpers.getValueOrDefault = helpers.valueOrDefault;
+
+/**
+ * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead.
+ * @function Chart.helpers.getValueAtIndexOrDefault
+ * @deprecated since version 2.7.0
+ * @todo remove at version 3
+ * @private
+ */
+helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
+
+/**
+ * Easing functions adapted from Robert Penner's easing equations.
+ * @namespace Chart.helpers.easingEffects
+ * @see http://www.robertpenner.com/easing/
+ */
+var effects = {
+ linear: function(t) {
+ return t;
+ },
+
+ easeInQuad: function(t) {
+ return t * t;
+ },
+
+ easeOutQuad: function(t) {
+ return -t * (t - 2);
+ },
+
+ easeInOutQuad: function(t) {
+ if ((t /= 0.5) < 1) {
+ return 0.5 * t * t;
+ }
+ return -0.5 * ((--t) * (t - 2) - 1);
+ },
+
+ easeInCubic: function(t) {
+ return t * t * t;
+ },
+
+ easeOutCubic: function(t) {
+ return (t = t - 1) * t * t + 1;
+ },
+
+ easeInOutCubic: function(t) {
+ if ((t /= 0.5) < 1) {
+ return 0.5 * t * t * t;
+ }
+ return 0.5 * ((t -= 2) * t * t + 2);
+ },
+
+ easeInQuart: function(t) {
+ return t * t * t * t;
+ },
+
+ easeOutQuart: function(t) {
+ return -((t = t - 1) * t * t * t - 1);
+ },
+
+ easeInOutQuart: function(t) {
+ if ((t /= 0.5) < 1) {
+ return 0.5 * t * t * t * t;
+ }
+ return -0.5 * ((t -= 2) * t * t * t - 2);
+ },
+
+ easeInQuint: function(t) {
+ return t * t * t * t * t;
+ },
+
+ easeOutQuint: function(t) {
+ return (t = t - 1) * t * t * t * t + 1;
+ },
+
+ easeInOutQuint: function(t) {
+ if ((t /= 0.5) < 1) {
+ return 0.5 * t * t * t * t * t;
+ }
+ return 0.5 * ((t -= 2) * t * t * t * t + 2);
+ },
+
+ easeInSine: function(t) {
+ return -Math.cos(t * (Math.PI / 2)) + 1;
+ },
+
+ easeOutSine: function(t) {
+ return Math.sin(t * (Math.PI / 2));
+ },
+
+ easeInOutSine: function(t) {
+ return -0.5 * (Math.cos(Math.PI * t) - 1);
+ },
+
+ easeInExpo: function(t) {
+ return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));
+ },
+
+ easeOutExpo: function(t) {
+ return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1;
+ },
+
+ easeInOutExpo: function(t) {
+ if (t === 0) {
+ return 0;
+ }
+ if (t === 1) {
+ return 1;
+ }
+ if ((t /= 0.5) < 1) {
+ return 0.5 * Math.pow(2, 10 * (t - 1));
+ }
+ return 0.5 * (-Math.pow(2, -10 * --t) + 2);
+ },
+
+ easeInCirc: function(t) {
+ if (t >= 1) {
+ return t;
+ }
+ return -(Math.sqrt(1 - t * t) - 1);
+ },
+
+ easeOutCirc: function(t) {
+ return Math.sqrt(1 - (t = t - 1) * t);
+ },
+
+ easeInOutCirc: function(t) {
+ if ((t /= 0.5) < 1) {
+ return -0.5 * (Math.sqrt(1 - t * t) - 1);
+ }
+ return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1);
+ },
+
+ easeInElastic: function(t) {
+ var s = 1.70158;
+ var p = 0;
+ var a = 1;
+ if (t === 0) {
+ return 0;
+ }
+ if (t === 1) {
+ return 1;
+ }
+ if (!p) {
+ p = 0.3;
+ }
+ if (a < 1) {
+ a = 1;
+ s = p / 4;
+ } else {
+ s = p / (2 * Math.PI) * Math.asin(1 / a);
+ }
+ return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
+ },
+
+ easeOutElastic: function(t) {
+ var s = 1.70158;
+ var p = 0;
+ var a = 1;
+ if (t === 0) {
+ return 0;
+ }
+ if (t === 1) {
+ return 1;
+ }
+ if (!p) {
+ p = 0.3;
+ }
+ if (a < 1) {
+ a = 1;
+ s = p / 4;
+ } else {
+ s = p / (2 * Math.PI) * Math.asin(1 / a);
+ }
+ return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1;
+ },
+
+ easeInOutElastic: function(t) {
+ var s = 1.70158;
+ var p = 0;
+ var a = 1;
+ if (t === 0) {
+ return 0;
+ }
+ if ((t /= 0.5) === 2) {
+ return 1;
+ }
+ if (!p) {
+ p = 0.45;
+ }
+ if (a < 1) {
+ a = 1;
+ s = p / 4;
+ } else {
+ s = p / (2 * Math.PI) * Math.asin(1 / a);
+ }
+ if (t < 1) {
+ return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
+ }
+ return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1;
+ },
+ easeInBack: function(t) {
+ var s = 1.70158;
+ return t * t * ((s + 1) * t - s);
+ },
+
+ easeOutBack: function(t) {
+ var s = 1.70158;
+ return (t = t - 1) * t * ((s + 1) * t + s) + 1;
+ },
+
+ easeInOutBack: function(t) {
+ var s = 1.70158;
+ if ((t /= 0.5) < 1) {
+ return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s));
+ }
+ return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
+ },
+
+ easeInBounce: function(t) {
+ return 1 - effects.easeOutBounce(1 - t);
+ },
+
+ easeOutBounce: function(t) {
+ if (t < (1 / 2.75)) {
+ return 7.5625 * t * t;
+ }
+ if (t < (2 / 2.75)) {
+ return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75;
+ }
+ if (t < (2.5 / 2.75)) {
+ return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375;
+ }
+ return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375;
+ },
+
+ easeInOutBounce: function(t) {
+ if (t < 0.5) {
+ return effects.easeInBounce(t * 2) * 0.5;
+ }
+ return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5;
+ }
+};
+
+var helpers_easing = {
+ effects: effects
+};
+
+// DEPRECATIONS
+
+/**
+ * Provided for backward compatibility, use Chart.helpers.easing.effects instead.
+ * @function Chart.helpers.easingEffects
+ * @deprecated since version 2.7.0
+ * @todo remove at version 3
+ * @private
+ */
+helpers_core.easingEffects = effects;
+
+var PI = Math.PI;
+var RAD_PER_DEG = PI / 180;
+var DOUBLE_PI = PI * 2;
+var HALF_PI = PI / 2;
+var QUARTER_PI = PI / 4;
+var TWO_THIRDS_PI = PI * 2 / 3;
+
+/**
+ * @namespace Chart.helpers.canvas
+ */
+var exports$1 = {
+ /**
+ * Clears the entire canvas associated to the given `chart`.
+ * @param {Chart} chart - The chart for which to clear the canvas.
+ */
+ clear: function(chart) {
+ chart.ctx.clearRect(0, 0, chart.width, chart.height);
+ },
+
+ /**
+ * Creates a "path" for a rectangle with rounded corners at position (x, y) with a
+ * given size (width, height) and the same `radius` for all corners.
+ * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context.
+ * @param {number} x - The x axis of the coordinate for the rectangle starting point.
+ * @param {number} y - The y axis of the coordinate for the rectangle starting point.
+ * @param {number} width - The rectangle's width.
+ * @param {number} height - The rectangle's height.
+ * @param {number} radius - The rounded amount (in pixels) for the four corners.
+ * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object?
+ */
+ roundedRect: function(ctx, x, y, width, height, radius) {
+ if (radius) {
+ var r = Math.min(radius, height / 2, width / 2);
+ var left = x + r;
+ var top = y + r;
+ var right = x + width - r;
+ var bottom = y + height - r;
+
+ ctx.moveTo(x, top);
+ if (left < right && top < bottom) {
+ ctx.arc(left, top, r, -PI, -HALF_PI);
+ ctx.arc(right, top, r, -HALF_PI, 0);
+ ctx.arc(right, bottom, r, 0, HALF_PI);
+ ctx.arc(left, bottom, r, HALF_PI, PI);
+ } else if (left < right) {
+ ctx.moveTo(left, y);
+ ctx.arc(right, top, r, -HALF_PI, HALF_PI);
+ ctx.arc(left, top, r, HALF_PI, PI + HALF_PI);
+ } else if (top < bottom) {
+ ctx.arc(left, top, r, -PI, 0);
+ ctx.arc(left, bottom, r, 0, PI);
+ } else {
+ ctx.arc(left, top, r, -PI, PI);
+ }
+ ctx.closePath();
+ ctx.moveTo(x, y);
+ } else {
+ ctx.rect(x, y, width, height);
+ }
+ },
+
+ drawPoint: function(ctx, style, radius, x, y, rotation) {
+ var type, xOffset, yOffset, size, cornerRadius;
+ var rad = (rotation || 0) * RAD_PER_DEG;
+
+ if (style && typeof style === 'object') {
+ type = style.toString();
+ if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
+ ctx.save();
+ ctx.translate(x, y);
+ ctx.rotate(rad);
+ ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height);
+ ctx.restore();
+ return;
+ }
+ }
+
+ if (isNaN(radius) || radius <= 0) {
+ return;
+ }
+
+ ctx.beginPath();
+
+ switch (style) {
+ // Default includes circle
+ default:
+ ctx.arc(x, y, radius, 0, DOUBLE_PI);
+ ctx.closePath();
+ break;
+ case 'triangle':
+ ctx.moveTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
+ rad += TWO_THIRDS_PI;
+ ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
+ rad += TWO_THIRDS_PI;
+ ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
+ ctx.closePath();
+ break;
+ case 'rectRounded':
+ // NOTE: the rounded rect implementation changed to use `arc` instead of
+ // `quadraticCurveTo` since it generates better results when rect is
+ // almost a circle. 0.516 (instead of 0.5) produces results with visually
+ // closer proportion to the previous impl and it is inscribed in the
+ // circle with `radius`. For more details, see the following PRs:
+ // https://github.com/chartjs/Chart.js/issues/5597
+ // https://github.com/chartjs/Chart.js/issues/5858
+ cornerRadius = radius * 0.516;
+ size = radius - cornerRadius;
+ xOffset = Math.cos(rad + QUARTER_PI) * size;
+ yOffset = Math.sin(rad + QUARTER_PI) * size;
+ ctx.arc(x - xOffset, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);
+ ctx.arc(x + yOffset, y - xOffset, cornerRadius, rad - HALF_PI, rad);
+ ctx.arc(x + xOffset, y + yOffset, cornerRadius, rad, rad + HALF_PI);
+ ctx.arc(x - yOffset, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);
+ ctx.closePath();
+ break;
+ case 'rect':
+ if (!rotation) {
+ size = Math.SQRT1_2 * radius;
+ ctx.rect(x - size, y - size, 2 * size, 2 * size);
+ break;
+ }
+ rad += QUARTER_PI;
+ /* falls through */
+ case 'rectRot':
+ xOffset = Math.cos(rad) * radius;
+ yOffset = Math.sin(rad) * radius;
+ ctx.moveTo(x - xOffset, y - yOffset);
+ ctx.lineTo(x + yOffset, y - xOffset);
+ ctx.lineTo(x + xOffset, y + yOffset);
+ ctx.lineTo(x - yOffset, y + xOffset);
+ ctx.closePath();
+ break;
+ case 'crossRot':
+ rad += QUARTER_PI;
+ /* falls through */
+ case 'cross':
+ xOffset = Math.cos(rad) * radius;
+ yOffset = Math.sin(rad) * radius;
+ ctx.moveTo(x - xOffset, y - yOffset);
+ ctx.lineTo(x + xOffset, y + yOffset);
+ ctx.moveTo(x + yOffset, y - xOffset);
+ ctx.lineTo(x - yOffset, y + xOffset);
+ break;
+ case 'star':
+ xOffset = Math.cos(rad) * radius;
+ yOffset = Math.sin(rad) * radius;
+ ctx.moveTo(x - xOffset, y - yOffset);
+ ctx.lineTo(x + xOffset, y + yOffset);
+ ctx.moveTo(x + yOffset, y - xOffset);
+ ctx.lineTo(x - yOffset, y + xOffset);
+ rad += QUARTER_PI;
+ xOffset = Math.cos(rad) * radius;
+ yOffset = Math.sin(rad) * radius;
+ ctx.moveTo(x - xOffset, y - yOffset);
+ ctx.lineTo(x + xOffset, y + yOffset);
+ ctx.moveTo(x + yOffset, y - xOffset);
+ ctx.lineTo(x - yOffset, y + xOffset);
+ break;
+ case 'line':
+ xOffset = Math.cos(rad) * radius;
+ yOffset = Math.sin(rad) * radius;
+ ctx.moveTo(x - xOffset, y - yOffset);
+ ctx.lineTo(x + xOffset, y + yOffset);
+ break;
+ case 'dash':
+ ctx.moveTo(x, y);
+ ctx.lineTo(x + Math.cos(rad) * radius, y + Math.sin(rad) * radius);
+ break;
+ }
+
+ ctx.fill();
+ ctx.stroke();
+ },
+
+ /**
+ * Returns true if the point is inside the rectangle
+ * @param {object} point - The point to test
+ * @param {object} area - The rectangle
+ * @returns {boolean}
+ * @private
+ */
+ _isPointInArea: function(point, area) {
+ var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error.
+
+ return point.x > area.left - epsilon && point.x < area.right + epsilon &&
+ point.y > area.top - epsilon && point.y < area.bottom + epsilon;
+ },
+
+ clipArea: function(ctx, area) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
+ ctx.clip();
+ },
+
+ unclipArea: function(ctx) {
+ ctx.restore();
+ },
+
+ lineTo: function(ctx, previous, target, flip) {
+ var stepped = target.steppedLine;
+ if (stepped) {
+ if (stepped === 'middle') {
+ var midpoint = (previous.x + target.x) / 2.0;
+ ctx.lineTo(midpoint, flip ? target.y : previous.y);
+ ctx.lineTo(midpoint, flip ? previous.y : target.y);
+ } else if ((stepped === 'after' && !flip) || (stepped !== 'after' && flip)) {
+ ctx.lineTo(previous.x, target.y);
+ } else {
+ ctx.lineTo(target.x, previous.y);
+ }
+ ctx.lineTo(target.x, target.y);
+ return;
+ }
+
+ if (!target.tension) {
+ ctx.lineTo(target.x, target.y);
+ return;
+ }
+
+ ctx.bezierCurveTo(
+ flip ? previous.controlPointPreviousX : previous.controlPointNextX,
+ flip ? previous.controlPointPreviousY : previous.controlPointNextY,
+ flip ? target.controlPointNextX : target.controlPointPreviousX,
+ flip ? target.controlPointNextY : target.controlPointPreviousY,
+ target.x,
+ target.y);
+ }
+};
+
+var helpers_canvas = exports$1;
+
+// DEPRECATIONS
+
+/**
+ * Provided for backward compatibility, use Chart.helpers.canvas.clear instead.
+ * @namespace Chart.helpers.clear
+ * @deprecated since version 2.7.0
+ * @todo remove at version 3
+ * @private
+ */
+helpers_core.clear = exports$1.clear;
+
+/**
+ * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead.
+ * @namespace Chart.helpers.drawRoundedRectangle
+ * @deprecated since version 2.7.0
+ * @todo remove at version 3
+ * @private
+ */
+helpers_core.drawRoundedRectangle = function(ctx) {
+ ctx.beginPath();
+ exports$1.roundedRect.apply(exports$1, arguments);
+};
+
+var defaults = {
+ /**
+ * @private
+ */
+ _set: function(scope, values) {
+ return helpers_core.merge(this[scope] || (this[scope] = {}), values);
+ }
+};
+
+// TODO(v3): remove 'global' from namespace. all default are global and
+// there's inconsistency around which options are under 'global'
+defaults._set('global', {
+ defaultColor: 'rgba(0,0,0,0.1)',
+ defaultFontColor: '#666',
+ defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
+ defaultFontSize: 12,
+ defaultFontStyle: 'normal',
+ defaultLineHeight: 1.2,
+ showLines: true
+});
+
+var core_defaults = defaults;
+
+var valueOrDefault = helpers_core.valueOrDefault;
+
+/**
+ * Converts the given font object into a CSS font string.
+ * @param {object} font - A font object.
+ * @return {string} The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font
+ * @private
+ */
+function toFontString(font) {
+ if (!font || helpers_core.isNullOrUndef(font.size) || helpers_core.isNullOrUndef(font.family)) {
+ return null;
+ }
+
+ return (font.style ? font.style + ' ' : '')
+ + (font.weight ? font.weight + ' ' : '')
+ + font.size + 'px '
+ + font.family;
+}
+
+/**
+ * @alias Chart.helpers.options
+ * @namespace
+ */
+var helpers_options = {
+ /**
+ * Converts the given line height `value` in pixels for a specific font `size`.
+ * @param {number|string} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').
+ * @param {number} size - The font size (in pixels) used to resolve relative `value`.
+ * @returns {number} The effective line height in pixels (size * 1.2 if value is invalid).
+ * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
+ * @since 2.7.0
+ */
+ toLineHeight: function(value, size) {
+ var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);
+ if (!matches || matches[1] === 'normal') {
+ return size * 1.2;
+ }
+
+ value = +matches[2];
+
+ switch (matches[3]) {
+ case 'px':
+ return value;
+ case '%':
+ value /= 100;
+ break;
+ }
+
+ return size * value;
+ },
+
+ /**
+ * Converts the given value into a padding object with pre-computed width/height.
+ * @param {number|object} value - If a number, set the value to all TRBL component,
+ * else, if and object, use defined properties and sets undefined ones to 0.
+ * @returns {object} The padding values (top, right, bottom, left, width, height)
+ * @since 2.7.0
+ */
+ toPadding: function(value) {
+ var t, r, b, l;
+
+ if (helpers_core.isObject(value)) {
+ t = +value.top || 0;
+ r = +value.right || 0;
+ b = +value.bottom || 0;
+ l = +value.left || 0;
+ } else {
+ t = r = b = l = +value || 0;
+ }
+
+ return {
+ top: t,
+ right: r,
+ bottom: b,
+ left: l,
+ height: t + b,
+ width: l + r
+ };
+ },
+
+ /**
+ * Parses font options and returns the font object.
+ * @param {object} options - A object that contains font options to be parsed.
+ * @return {object} The font object.
+ * @todo Support font.* options and renamed to toFont().
+ * @private
+ */
+ _parseFont: function(options) {
+ var globalDefaults = core_defaults.global;
+ var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize);
+ var font = {
+ family: valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily),
+ lineHeight: helpers_core.options.toLineHeight(valueOrDefault(options.lineHeight, globalDefaults.defaultLineHeight), size),
+ size: size,
+ style: valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle),
+ weight: null,
+ string: ''
+ };
+
+ font.string = toFontString(font);
+ return font;
+ },
+
+ /**
+ * Evaluates the given `inputs` sequentially and returns the first defined value.
+ * @param {Array} inputs - An array of values, falling back to the last value.
+ * @param {object} [context] - If defined and the current value is a function, the value
+ * is called with `context` as first argument and the result becomes the new input.
+ * @param {number} [index] - If defined and the current value is an array, the value
+ * at `index` become the new input.
+ * @param {object} [info] - object to return information about resolution in
+ * @param {boolean} [info.cacheable] - Will be set to `false` if option is not cacheable.
+ * @since 2.7.0
+ */
+ resolve: function(inputs, context, index, info) {
+ var cacheable = true;
+ var i, ilen, value;
+
+ for (i = 0, ilen = inputs.length; i < ilen; ++i) {
+ value = inputs[i];
+ if (value === undefined) {
+ continue;
+ }
+ if (context !== undefined && typeof value === 'function') {
+ value = value(context);
+ cacheable = false;
+ }
+ if (index !== undefined && helpers_core.isArray(value)) {
+ value = value[index];
+ cacheable = false;
+ }
+ if (value !== undefined) {
+ if (info && !cacheable) {
+ info.cacheable = false;
+ }
+ return value;
+ }
+ }
+ }
+};
+
+/**
+ * @alias Chart.helpers.math
+ * @namespace
+ */
+var exports$2 = {
+ /**
+ * Returns an array of factors sorted from 1 to sqrt(value)
+ * @private
+ */
+ _factorize: function(value) {
+ var result = [];
+ var sqrt = Math.sqrt(value);
+ var i;
+
+ for (i = 1; i < sqrt; i++) {
+ if (value % i === 0) {
+ result.push(i);
+ result.push(value / i);
+ }
+ }
+ if (sqrt === (sqrt | 0)) { // if value is a square number
+ result.push(sqrt);
+ }
+
+ result.sort(function(a, b) {
+ return a - b;
+ }).pop();
+ return result;
+ },
+
+ log10: Math.log10 || function(x) {
+ var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10.
+ // Check for whole powers of 10,
+ // which due to floating point rounding error should be corrected.
+ var powerOf10 = Math.round(exponent);
+ var isPowerOf10 = x === Math.pow(10, powerOf10);
+
+ return isPowerOf10 ? powerOf10 : exponent;
+ }
+};
+
+var helpers_math = exports$2;
+
+// DEPRECATIONS
+
+/**
+ * Provided for backward compatibility, use Chart.helpers.math.log10 instead.
+ * @namespace Chart.helpers.log10
+ * @deprecated since version 2.9.0
+ * @todo remove at version 3
+ * @private
+ */
+helpers_core.log10 = exports$2.log10;
+
+var getRtlAdapter = function(rectX, width) {
+ return {
+ x: function(x) {
+ return rectX + rectX + width - x;
+ },
+ setWidth: function(w) {
+ width = w;
+ },
+ textAlign: function(align) {
+ if (align === 'center') {
+ return align;
+ }
+ return align === 'right' ? 'left' : 'right';
+ },
+ xPlus: function(x, value) {
+ return x - value;
+ },
+ leftForLtr: function(x, itemWidth) {
+ return x - itemWidth;
+ },
+ };
+};
+
+var getLtrAdapter = function() {
+ return {
+ x: function(x) {
+ return x;
+ },
+ setWidth: function(w) { // eslint-disable-line no-unused-vars
+ },
+ textAlign: function(align) {
+ return align;
+ },
+ xPlus: function(x, value) {
+ return x + value;
+ },
+ leftForLtr: function(x, _itemWidth) { // eslint-disable-line no-unused-vars
+ return x;
+ },
+ };
+};
+
+var getAdapter = function(rtl, rectX, width) {
+ return rtl ? getRtlAdapter(rectX, width) : getLtrAdapter();
+};
+
+var overrideTextDirection = function(ctx, direction) {
+ var style, original;
+ if (direction === 'ltr' || direction === 'rtl') {
+ style = ctx.canvas.style;
+ original = [
+ style.getPropertyValue('direction'),
+ style.getPropertyPriority('direction'),
+ ];
+
+ style.setProperty('direction', direction, 'important');
+ ctx.prevTextDirection = original;
+ }
+};
+
+var restoreTextDirection = function(ctx) {
+ var original = ctx.prevTextDirection;
+ if (original !== undefined) {
+ delete ctx.prevTextDirection;
+ ctx.canvas.style.setProperty('direction', original[0], original[1]);
+ }
+};
+
+var helpers_rtl = {
+ getRtlAdapter: getAdapter,
+ overrideTextDirection: overrideTextDirection,
+ restoreTextDirection: restoreTextDirection,
+};
+
+var helpers$1 = helpers_core;
+var easing = helpers_easing;
+var canvas = helpers_canvas;
+var options = helpers_options;
+var math = helpers_math;
+var rtl = helpers_rtl;
+helpers$1.easing = easing;
+helpers$1.canvas = canvas;
+helpers$1.options = options;
+helpers$1.math = math;
+helpers$1.rtl = rtl;
+
+function interpolate(start, view, model, ease) {
+ var keys = Object.keys(model);
+ var i, ilen, key, actual, origin, target, type, c0, c1;
+
+ for (i = 0, ilen = keys.length; i < ilen; ++i) {
+ key = keys[i];
+
+ target = model[key];
+
+ // if a value is added to the model after pivot() has been called, the view
+ // doesn't contain it, so let's initialize the view to the target value.
+ if (!view.hasOwnProperty(key)) {
+ view[key] = target;
+ }
+
+ actual = view[key];
+
+ if (actual === target || key[0] === '_') {
+ continue;
+ }
+
+ if (!start.hasOwnProperty(key)) {
+ start[key] = actual;
+ }
+
+ origin = start[key];
+
+ type = typeof target;
+
+ if (type === typeof origin) {
+ if (type === 'string') {
+ c0 = chartjsColor(origin);
+ if (c0.valid) {
+ c1 = chartjsColor(target);
+ if (c1.valid) {
+ view[key] = c1.mix(c0, ease).rgbString();
+ continue;
+ }
+ }
+ } else if (helpers$1.isFinite(origin) && helpers$1.isFinite(target)) {
+ view[key] = origin + (target - origin) * ease;
+ continue;
+ }
+ }
+
+ view[key] = target;
+ }
+}
+
+var Element = function(configuration) {
+ helpers$1.extend(this, configuration);
+ this.initialize.apply(this, arguments);
+};
+
+helpers$1.extend(Element.prototype, {
+ _type: undefined,
+
+ initialize: function() {
+ this.hidden = false;
+ },
+
+ pivot: function() {
+ var me = this;
+ if (!me._view) {
+ me._view = helpers$1.extend({}, me._model);
+ }
+ me._start = {};
+ return me;
+ },
+
+ transition: function(ease) {
+ var me = this;
+ var model = me._model;
+ var start = me._start;
+ var view = me._view;
+
+ // No animation -> No Transition
+ if (!model || ease === 1) {
+ me._view = helpers$1.extend({}, model);
+ me._start = null;
+ return me;
+ }
+
+ if (!view) {
+ view = me._view = {};
+ }
+
+ if (!start) {
+ start = me._start = {};
+ }
+
+ interpolate(start, view, model, ease);
+
+ return me;
+ },
+
+ tooltipPosition: function() {
+ return {
+ x: this._model.x,
+ y: this._model.y
+ };
+ },
+
+ hasValue: function() {
+ return helpers$1.isNumber(this._model.x) && helpers$1.isNumber(this._model.y);
+ }
+});
+
+Element.extend = helpers$1.inherits;
+
+var core_element = Element;
+
+var exports$3 = core_element.extend({
+ chart: null, // the animation associated chart instance
+ currentStep: 0, // the current animation step
+ numSteps: 60, // default number of steps
+ easing: '', // the easing to use for this animation
+ render: null, // render function used by the animation service
+
+ onAnimationProgress: null, // user specified callback to fire on each step of the animation
+ onAnimationComplete: null, // user specified callback to fire when the animation finishes
+});
+
+var core_animation = exports$3;
+
+// DEPRECATIONS
+
+/**
+ * Provided for backward compatibility, use Chart.Animation instead
+ * @prop Chart.Animation#animationObject
+ * @deprecated since version 2.6.0
+ * @todo remove at version 3
+ */
+Object.defineProperty(exports$3.prototype, 'animationObject', {
+ get: function() {
+ return this;
+ }
+});
+
+/**
+ * Provided for backward compatibility, use Chart.Animation#chart instead
+ * @prop Chart.Animation#chartInstance
+ * @deprecated since version 2.6.0
+ * @todo remove at version 3
+ */
+Object.defineProperty(exports$3.prototype, 'chartInstance', {
+ get: function() {
+ return this.chart;
+ },
+ set: function(value) {
+ this.chart = value;
+ }
+});
+
+core_defaults._set('global', {
+ animation: {
+ duration: 1000,
+ easing: 'easeOutQuart',
+ onProgress: helpers$1.noop,
+ onComplete: helpers$1.noop
+ }
+});
+
+var core_animations = {
+ animations: [],
+ request: null,
+
+ /**
+ * @param {Chart} chart - The chart to animate.
+ * @param {Chart.Animation} animation - The animation that we will animate.
+ * @param {number} duration - The animation duration in ms.
+ * @param {boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions
+ */
+ addAnimation: function(chart, animation, duration, lazy) {
+ var animations = this.animations;
+ var i, ilen;
+
+ animation.chart = chart;
+ animation.startTime = Date.now();
+ animation.duration = duration;
+
+ if (!lazy) {
+ chart.animating = true;
+ }
+
+ for (i = 0, ilen = animations.length; i < ilen; ++i) {
+ if (animations[i].chart === chart) {
+ animations[i] = animation;
+ return;
+ }
+ }
+
+ animations.push(animation);
+
+ // If there are no animations queued, manually kickstart a digest, for lack of a better word
+ if (animations.length === 1) {
+ this.requestAnimationFrame();
+ }
+ },
+
+ cancelAnimation: function(chart) {
+ var index = helpers$1.findIndex(this.animations, function(animation) {
+ return animation.chart === chart;
+ });
+
+ if (index !== -1) {
+ this.animations.splice(index, 1);
+ chart.animating = false;
+ }
+ },
+
+ requestAnimationFrame: function() {
+ var me = this;
+ if (me.request === null) {
+ return;
+ // TBD: animation should work somehow; what is startDigest?
+
+ // Skip animation frame requests until the active one is executed.
+ // This can happen when processing mouse events, e.g. 'mousemove'
+ // and 'mouseout' events will trigger multiple renders.
+ me.request = helpers$1.requestAnimFrame.call(undefined, function() {
+ me.request = null;
+ me.startDigest();
+ });
+ }
+ },
+
+ /**
+ * @private
+ */
+ startDigest: function() {
+ var me = this;
+
+ me.advance();
+
+ // Do we have more stuff to animate?
+ if (me.animations.length > 0) {
+ me.requestAnimationFrame();
+ }
+ },
+
+ /**
+ * @private
+ */
+ advance: function() {
+ var animations = this.animations;
+ var animation, chart, numSteps, nextStep;
+ var i = 0;
+
+ // 1 animation per chart, so we are looping charts here
+ while (i < animations.length) {
+ animation = animations[i];
+ chart = animation.chart;
+ numSteps = animation.numSteps;
+
+ // Make sure that currentStep starts at 1
+ // https://github.com/chartjs/Chart.js/issues/6104
+ nextStep = Math.floor((Date.now() - animation.startTime) / animation.duration * numSteps) + 1;
+ animation.currentStep = Math.min(nextStep, numSteps);
+
+ helpers$1.callback(animation.render, [chart, animation], chart);
+ helpers$1.callback(animation.onAnimationProgress, [animation], chart);
+
+ if (animation.currentStep >= numSteps) {
+ helpers$1.callback(animation.onAnimationComplete, [animation], chart);
+ chart.animating = false;
+ animations.splice(i, 1);
+ } else {
+ ++i;
+ }
+ }
+ }
+};
+
+var resolve = helpers$1.options.resolve;
+
+var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];
+
+/**
+ * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',
+ * 'unshift') and notify the listener AFTER the array has been altered. Listeners are
+ * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments.
+ */
+function listenArrayEvents(array, listener) {
+ if (array._chartjs) {
+ array._chartjs.listeners.push(listener);
+ return;
+ }
+
+ Object.defineProperty(array, '_chartjs', {
+ configurable: true,
+ enumerable: false,
+ value: {
+ listeners: [listener]
+ }
+ });
+
+ arrayEvents.forEach(function(key) {
+ var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1);
+ var base = array[key];
+
+ Object.defineProperty(array, key, {
+ configurable: true,
+ enumerable: false,
+ value: function() {
+ var args = Array.prototype.slice.call(arguments);
+ var res = base.apply(this, args);
+
+ helpers$1.each(array._chartjs.listeners, function(object) {
+ if (typeof object[method] === 'function') {
+ object[method].apply(object, args);
+ }
+ });
+
+ return res;
+ }
+ });
+ });
+}
+
+/**
+ * Removes the given array event listener and cleanup extra attached properties (such as
+ * the _chartjs stub and overridden methods) if array doesn't have any more listeners.
+ */
+function unlistenArrayEvents(array, listener) {
+ var stub = array._chartjs;
+ if (!stub) {
+ return;
+ }
+
+ var listeners = stub.listeners;
+ var index = listeners.indexOf(listener);
+ if (index !== -1) {
+ listeners.splice(index, 1);
+ }
+
+ if (listeners.length > 0) {
+ return;
+ }
+
+ arrayEvents.forEach(function(key) {
+ delete array[key];
+ });
+
+ delete array._chartjs;
+}
+
+// Base class for all dataset controllers (line, bar, etc)
+var DatasetController = function(chart, datasetIndex) {
+ this.initialize(chart, datasetIndex);
+};
+
+helpers$1.extend(DatasetController.prototype, {
+
+ /**
+ * Element type used to generate a meta dataset (e.g. Chart.element.Line).
+ * @type {Chart.core.element}
+ */
+ datasetElementType: null,
+
+ /**
+ * Element type used to generate a meta data (e.g. Chart.element.Point).
+ * @type {Chart.core.element}
+ */
+ dataElementType: null,
+
+ /**
+ * Dataset element option keys to be resolved in _resolveDatasetElementOptions.
+ * A derived controller may override this to resolve controller-specific options.
+ * The keys defined here are for backward compatibility for legend styles.
+ * @private
+ */
+ _datasetElementOptions: [
+ 'backgroundColor',
+ 'borderCapStyle',
+ 'borderColor',
+ 'borderDash',
+ 'borderDashOffset',
+ 'borderJoinStyle',
+ 'borderWidth'
+ ],
+
+ /**
+ * Data element option keys to be resolved in _resolveDataElementOptions.
+ * A derived controller may override this to resolve controller-specific options.
+ * The keys defined here are for backward compatibility for legend styles.
+ * @private
+ */
+ _dataElementOptions: [
+ 'backgroundColor',
+ 'borderColor',
+ 'borderWidth',
+ 'pointStyle'
+ ],
+
+ initialize: function(chart, datasetIndex) {
+ var me = this;
+ me.chart = chart;
+ me.index = datasetIndex;
+ me.linkScales();
+ me.addElements();
+ me._type = me.getMeta().type;
+ },
+
+ updateIndex: function(datasetIndex) {
+ this.index = datasetIndex;
+ },
+
+ linkScales: function() {
+ var me = this;
+ var meta = me.getMeta();
+ var chart = me.chart;
+ var scales = chart.scales;
+ var dataset = me.getDataset();
+ var scalesOpts = chart.options.scales;
+
+ if (meta.xAxisID === null || !(meta.xAxisID in scales) || dataset.xAxisID) {
+ meta.xAxisID = dataset.xAxisID || scalesOpts.xAxes[0].id;
+ }
+ if (meta.yAxisID === null || !(meta.yAxisID in scales) || dataset.yAxisID) {
+ meta.yAxisID = dataset.yAxisID || scalesOpts.yAxes[0].id;
+ }
+ },
+
+ getDataset: function() {
+ return this.chart.data.datasets[this.index];
+ },
+
+ getMeta: function() {
+ return this.chart.getDatasetMeta(this.index);
+ },
+
+ getScaleForId: function(scaleID) {
+ return this.chart.scales[scaleID];
+ },
+
+ /**
+ * @private
+ */
+ _getValueScaleId: function() {
+ return this.getMeta().yAxisID;
+ },
+
+ /**
+ * @private
+ */
+ _getIndexScaleId: function() {
+ return this.getMeta().xAxisID;
+ },
+
+ /**
+ * @private
+ */
+ _getValueScale: function() {
+ return this.getScaleForId(this._getValueScaleId());
+ },
+
+ /**
+ * @private
+ */
+ _getIndexScale: function() {
+ return this.getScaleForId(this._getIndexScaleId());
+ },
+
+ reset: function() {
+ this._update(true);
+ },
+
+ /**
+ * @private
+ */
+ destroy: function() {
+ if (this._data) {
+ unlistenArrayEvents(this._data, this);
+ }
+ },
+
+ createMetaDataset: function() {
+ var me = this;
+ var type = me.datasetElementType;
+ return type && new type({
+ _chart: me.chart,
+ _datasetIndex: me.index
+ });
+ },
+
+ createMetaData: function(index) {
+ var me = this;
+ var type = me.dataElementType;
+ return type && new type({
+ _chart: me.chart,
+ _datasetIndex: me.index,
+ _index: index
+ });
+ },
+
+ addElements: function() {
+ var me = this;
+ var meta = me.getMeta();
+ var data = me.getDataset().data || [];
+ var metaData = meta.data;
+ var i, ilen;
+
+ for (i = 0, ilen = data.length; i < ilen; ++i) {
+ metaData[i] = metaData[i] || me.createMetaData(i);
+ }
+
+ meta.dataset = meta.dataset || me.createMetaDataset();
+ },
+
+ addElementAndReset: function(index) {
+ var element = this.createMetaData(index);
+ this.getMeta().data.splice(index, 0, element);
+ this.updateElement(element, index, true);
+ },
+
+ buildOrUpdateElements: function() {
+ var me = this;
+ var dataset = me.getDataset();
+ var data = dataset.data || (dataset.data = []);
+
+ // In order to correctly handle data addition/deletion animation (an thus simulate
+ // real-time charts), we need to monitor these data modifications and synchronize
+ // the internal meta data accordingly.
+ if (me._data !== data) {
+ if (me._data) {
+ // This case happens when the user replaced the data array instance.
+ unlistenArrayEvents(me._data, me);
+ }
+
+ if (data && Object.isExtensible(data)) {
+ listenArrayEvents(data, me);
+ }
+ me._data = data;
+ }
+
+ // Re-sync meta data in case the user replaced the data array or if we missed
+ // any updates and so make sure that we handle number of datapoints changing.
+ me.resyncElements();
+ },
+
+ /**
+ * Returns the merged user-supplied and default dataset-level options
+ * @private
+ */
+ _configure: function() {
+ var me = this;
+ me._config = helpers$1.merge(Object.create(null), [
+ me.chart.options.datasets[me._type],
+ me.getDataset(),
+ ], {
+ merger: function(key, target, source) {
+ if (key !== '_meta' && key !== 'data') {
+ helpers$1._merger(key, target, source);
+ }
+ }
+ });
+ },
+
+ _update: function(reset) {
+ var me = this;
+ me._configure();
+ me._cachedDataOpts = null;
+ me.update(reset);
+ },
+
+ update: helpers$1.noop,
+
+ transition: function(easingValue) {
+ var meta = this.getMeta();
+ var elements = meta.data || [];
+ var ilen = elements.length;
+ var i = 0;
+
+ for (; i < ilen; ++i) {
+ elements[i].transition(easingValue);
+ }
+
+ if (meta.dataset) {
+ meta.dataset.transition(easingValue);
+ }
+ },
+
+ draw: function() {
+ var meta = this.getMeta();
+ var elements = meta.data || [];
+ var ilen = elements.length;
+ var i = 0;
+
+ if (meta.dataset) {
+ meta.dataset.draw();
+ }
+
+ for (; i < ilen; ++i) {
+ elements[i].draw();
+ }
+ },
+
+ /**
+ * Returns a set of predefined style properties that should be used to represent the dataset
+ * or the data if the index is specified
+ * @param {number} index - data index
+ * @return {IStyleInterface} style object
+ */
+ getStyle: function(index) {
+ var me = this;
+ var meta = me.getMeta();
+ var dataset = meta.dataset;
+ var style;
+
+ me._configure();
+ if (dataset && index === undefined) {
+ style = me._resolveDatasetElementOptions(dataset || {});
+ } else {
+ index = index || 0;
+ style = me._resolveDataElementOptions(meta.data[index] || {}, index);
+ }
+
+ if (style.fill === false || style.fill === null) {
+ style.backgroundColor = style.borderColor;
+ }
+
+ return style;
+ },
+
+ /**
+ * @private
+ */
+ _resolveDatasetElementOptions: function(element, hover) {
+ var me = this;
+ var chart = me.chart;
+ var datasetOpts = me._config;
+ var custom = element.custom || {};
+ var options = chart.options.elements[me.datasetElementType.prototype._type] || {};
+ var elementOptions = me._datasetElementOptions;
+ var values = {};
+ var i, ilen, key, readKey;
+
+ // Scriptable options
+ var context = {
+ chart: chart,
+ dataset: me.getDataset(),
+ datasetIndex: me.index,
+ hover: hover
+ };
+
+ for (i = 0, ilen = elementOptions.length; i < ilen; ++i) {
+ key = elementOptions[i];
+ readKey = hover ? 'hover' + key.charAt(0).toUpperCase() + key.slice(1) : key;
+ values[key] = resolve([
+ custom[readKey],
+ datasetOpts[readKey],
+ options[readKey]
+ ], context);
+ }
+
+ return values;
+ },
+
+ /**
+ * @private
+ */
+ _resolveDataElementOptions: function(element, index) {
+ var me = this;
+ var custom = element && element.custom;
+ var cached = me._cachedDataOpts;
+ if (cached && !custom) {
+ return cached;
+ }
+ var chart = me.chart;
+ var datasetOpts = me._config;
+ var options = chart.options.elements[me.dataElementType.prototype._type] || {};
+ var elementOptions = me._dataElementOptions;
+ var values = {};
+
+ // Scriptable options
+ var context = {
+ chart: chart,
+ dataIndex: index,
+ dataset: me.getDataset(),
+ datasetIndex: me.index
+ };
+
+ // `resolve` sets cacheable to `false` if any option is indexed or scripted
+ var info = {cacheable: !custom};
+
+ var keys, i, ilen, key;
+
+ custom = custom || {};
+
+ if (helpers$1.isArray(elementOptions)) {
+ for (i = 0, ilen = elementOptions.length; i < ilen; ++i) {
+ key = elementOptions[i];
+ values[key] = resolve([
+ custom[key],
+ datasetOpts[key],
+ options[key]
+ ], context, index, info);
+ }
+ } else {
+ keys = Object.keys(elementOptions);
+ for (i = 0, ilen = keys.length; i < ilen; ++i) {
+ key = keys[i];
+ values[key] = resolve([
+ custom[key],
+ datasetOpts[elementOptions[key]],
+ datasetOpts[key],
+ options[key]
+ ], context, index, info);
+ }
+ }
+
+ if (info.cacheable) {
+ me._cachedDataOpts = Object.freeze(values);
+ }
+
+ return values;
+ },
+
+ removeHoverStyle: function(element) {
+ helpers$1.merge(element._model, element.$previousStyle || {});
+ delete element.$previousStyle;
+ },
+
+ setHoverStyle: function(element) {
+ var dataset = this.chart.data.datasets[element._datasetIndex];
+ var index = element._index;
+ var custom = element.custom || {};
+ var model = element._model;
+ var getHoverColor = helpers$1.getHoverColor;
+
+ element.$previousStyle = {
+ backgroundColor: model.backgroundColor,
+ borderColor: model.borderColor,
+ borderWidth: model.borderWidth
+ };
+
+ model.backgroundColor = resolve([custom.hoverBackgroundColor, dataset.hoverBackgroundColor, getHoverColor(model.backgroundColor)], undefined, index);
+ model.borderColor = resolve([custom.hoverBorderColor, dataset.hoverBorderColor, getHoverColor(model.borderColor)], undefined, index);
+ model.borderWidth = resolve([custom.hoverBorderWidth, dataset.hoverBorderWidth, model.borderWidth], undefined, index);
+ },
+
+ /**
+ * @private
+ */
+ _removeDatasetHoverStyle: function() {
+ var element = this.getMeta().dataset;
+
+ if (element) {
+ this.removeHoverStyle(element);
+ }
+ },
+
+ /**
+ * @private
+ */
+ _setDatasetHoverStyle: function() {
+ var element = this.getMeta().dataset;
+ var prev = {};
+ var i, ilen, key, keys, hoverOptions, model;
+
+ if (!element) {
+ return;
+ }
+
+ model = element._model;
+ hoverOptions = this._resolveDatasetElementOptions(element, true);
+
+ keys = Object.keys(hoverOptions);
+ for (i = 0, ilen = keys.length; i < ilen; ++i) {
+ key = keys[i];
+ prev[key] = model[key];
+ model[key] = hoverOptions[key];
+ }
+
+ element.$previousStyle = prev;
+ },
+
+ /**
+ * @private
+ */
+ resyncElements: function() {
+ var me = this;
+ var meta = me.getMeta();
+ var data = me.getDataset().data;
+ var numMeta = meta.data.length;
+ var numData = data.length;
+
+ if (numData < numMeta) {
+ meta.data.splice(numData, numMeta - numData);
+ } else if (numData > numMeta) {
+ me.insertElements(numMeta, numData - numMeta);
+ }
+ },
+
+ /**
+ * @private
+ */
+ insertElements: function(start, count) {
+ for (var i = 0; i < count; ++i) {
+ this.addElementAndReset(start + i);
+ }
+ },
+
+ /**
+ * @private
+ */
+ onDataPush: function() {
+ var count = arguments.length;
+ this.insertElements(this.getDataset().data.length - count, count);
+ },
+
+ /**
+ * @private
+ */
+ onDataPop: function() {
+ this.getMeta().data.pop();
+ },
+
+ /**
+ * @private
+ */
+ onDataShift: function() {
+ this.getMeta().data.shift();
+ },
+
+ /**
+ * @private
+ */
+ onDataSplice: function(start, count) {
+ this.getMeta().data.splice(start, count);
+ this.insertElements(start, arguments.length - 2);
+ },
+
+ /**
+ * @private
+ */
+ onDataUnshift: function() {
+ this.insertElements(0, arguments.length);
+ }
+});
+
+DatasetController.extend = helpers$1.inherits;
+
+var core_datasetController = DatasetController;
+
+var TAU = Math.PI * 2;
+
+core_defaults._set('global', {
+ elements: {
+ arc: {
+ backgroundColor: core_defaults.global.defaultColor,
+ borderColor: '#fff',
+ borderWidth: 2,
+ borderAlign: 'center'
+ }
+ }
+});
+
+function clipArc(ctx, arc) {
+ var startAngle = arc.startAngle;
+ var endAngle = arc.endAngle;
+ var pixelMargin = arc.pixelMargin;
+ var angleMargin = pixelMargin / arc.outerRadius;
+ var x = arc.x;
+ var y = arc.y;
+
+ // Draw an inner border by cliping the arc and drawing a double-width border
+ // Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders
+ ctx.beginPath();
+ ctx.arc(x, y, arc.outerRadius, startAngle - angleMargin, endAngle + angleMargin);
+ if (arc.innerRadius > pixelMargin) {
+ angleMargin = pixelMargin / arc.innerRadius;
+ ctx.arc(x, y, arc.innerRadius - pixelMargin, endAngle + angleMargin, startAngle - angleMargin, true);
+ } else {
+ ctx.arc(x, y, pixelMargin, endAngle + Math.PI / 2, startAngle - Math.PI / 2);
+ }
+ ctx.closePath();
+ ctx.clip();
+}
+
+function drawFullCircleBorders(ctx, vm, arc, inner) {
+ var endAngle = arc.endAngle;
+ var i;
+
+ if (inner) {
+ arc.endAngle = arc.startAngle + TAU;
+ clipArc(ctx, arc);
+ arc.endAngle = endAngle;
+ if (arc.endAngle === arc.startAngle && arc.fullCircles) {
+ arc.endAngle += TAU;
+ arc.fullCircles--;
+ }
+ }
+
+ ctx.beginPath();
+ ctx.arc(arc.x, arc.y, arc.innerRadius, arc.startAngle + TAU, arc.startAngle, true);
+ for (i = 0; i < arc.fullCircles; ++i) {
+ ctx.stroke();
+ }
+
+ ctx.beginPath();
+ ctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.startAngle + TAU);
+ for (i = 0; i < arc.fullCircles; ++i) {
+ ctx.stroke();
+ }
+}
+
+function drawBorder(ctx, vm, arc) {
+ var inner = vm.borderAlign === 'inner';
+
+ if (inner) {
+ ctx.lineWidth = vm.borderWidth * 2;
+ ctx.lineJoin = 'round';
+ } else {
+ ctx.lineWidth = vm.borderWidth;
+ ctx.lineJoin = 'bevel';
+ }
+
+ if (arc.fullCircles) {
+ drawFullCircleBorders(ctx, vm, arc, inner);
+ }
+
+ if (inner) {
+ clipArc(ctx, arc);
+ }
+
+ ctx.beginPath();
+ ctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.endAngle);
+ ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true);
+ ctx.closePath();
+ ctx.stroke();
+}
+
+var element_arc = core_element.extend({
+ _type: 'arc',
+
+ inLabelRange: function(mouseX) {
+ var vm = this._view;
+
+ if (vm) {
+ return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
+ }
+ return false;
+ },
+
+ inRange: function(chartX, chartY) {
+ var vm = this._view;
+
+ if (vm) {
+ var pointRelativePosition = helpers$1.getAngleFromPoint(vm, {x: chartX, y: chartY});
+ var angle = pointRelativePosition.angle;
+ var distance = pointRelativePosition.distance;
+
+ // Sanitise angle range
+ var startAngle = vm.startAngle;
+ var endAngle = vm.endAngle;
+ while (endAngle < startAngle) {
+ endAngle += TAU;
+ }
+ while (angle > endAngle) {
+ angle -= TAU;
+ }
+ while (angle < startAngle) {
+ angle += TAU;
+ }
+
+ // Check if within the range of the open/close angle
+ var betweenAngles = (angle >= startAngle && angle <= endAngle);
+ var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);
+
+ return (betweenAngles && withinRadius);
+ }
+ return false;
+ },
+
+ getCenterPoint: function() {
+ var vm = this._view;
+ var halfAngle = (vm.startAngle + vm.endAngle) / 2;
+ var halfRadius = (vm.innerRadius + vm.outerRadius) / 2;
+ return {
+ x: vm.x + Math.cos(halfAngle) * halfRadius,
+ y: vm.y + Math.sin(halfAngle) * halfRadius
+ };
+ },
+
+ getArea: function() {
+ var vm = this._view;
+ return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2));
+ },
+
+ tooltipPosition: function() {
+ var vm = this._view;
+ var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2);
+ var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
+
+ return {
+ x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
+ y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
+ };
+ },
+
+ draw: function() {
+ var ctx = this._chart.ctx;
+ var vm = this._view;
+ var pixelMargin = (vm.borderAlign === 'inner') ? 0.33 : 0;
+ var arc = {
+ x: vm.x,
+ y: vm.y,
+ innerRadius: vm.innerRadius,
+ outerRadius: Math.max(vm.outerRadius - pixelMargin, 0),
+ pixelMargin: pixelMargin,
+ startAngle: vm.startAngle,
+ endAngle: vm.endAngle,
+ fullCircles: Math.floor(vm.circumference / TAU)
+ };
+ var i;
+
+ ctx.save();
+
+ ctx.fillStyle = vm.backgroundColor;
+ ctx.strokeStyle = vm.borderColor;
+
+ if (arc.fullCircles) {
+ arc.endAngle = arc.startAngle + TAU;
+ ctx.beginPath();
+ ctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle);
+ ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true);
+ ctx.closePath();
+ for (i = 0; i < arc.fullCircles; ++i) {
+ ctx.fill();
+ }
+ arc.endAngle = arc.startAngle + vm.circumference % TAU;
+ }
+
+ ctx.beginPath();
+ ctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle);
+ ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true);
+ ctx.closePath();
+ ctx.fill();
+
+ if (vm.borderWidth) {
+ drawBorder(ctx, vm, arc);
+ }
+
+ ctx.restore();
+ }
+});
+
+var valueOrDefault$1 = helpers$1.valueOrDefault;
+
+var defaultColor = core_defaults.global.defaultColor;
+
+core_defaults._set('global', {
+ elements: {
+ line: {
+ tension: 0.4,
+ backgroundColor: defaultColor,
+ borderWidth: 3,
+ borderColor: defaultColor,
+ borderCapStyle: 'butt',
+ borderDash: [],
+ borderDashOffset: 0.0,
+ borderJoinStyle: 'miter',
+ capBezierPoints: true,
+ fill: true, // do we fill in the area between the line and its base axis
+ }
+ }
+});
+
+var element_line = core_element.extend({
+ _type: 'line',
+
+ draw: function() {
+ var me = this;
+ var vm = me._view;
+ var ctx = me._chart.ctx;
+ var spanGaps = vm.spanGaps;
+ var points = me._children.slice(); // clone array
+ var globalDefaults = core_defaults.global;
+ var globalOptionLineElements = globalDefaults.elements.line;
+ var lastDrawnIndex = -1;
+ var closePath = me._loop;
+ var index, previous, currentVM;
+
+ if (!points.length) {
+ return;
+ }
+
+ if (me._loop) {
+ for (index = 0; index < points.length; ++index) {
+ previous = helpers$1.previousItem(points, index);
+ // If the line has an open path, shift the point array
+ if (!points[index]._view.skip && previous._view.skip) {
+ points = points.slice(index).concat(points.slice(0, index));
+ closePath = spanGaps;
+ break;
+ }
+ }
+ // If the line has a close path, add the first point again
+ if (closePath) {
+ points.push(points[0]);
+ }
+ }
+
+ ctx.save();
+
+ // Stroke Line Options
+ ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle;
+
+ // IE 9 and 10 do not support line dash
+ if (ctx.setLineDash) {
+ ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash);
+ }
+
+ // line dash fix for QML
+ if(ctx.getLineDash && ctx.getLineDash().length === 0) {
+ ctx.setLineDash([99999]);
+ }
+
+ ctx.lineDashOffset = valueOrDefault$1(vm.borderDashOffset, globalOptionLineElements.borderDashOffset);
+ ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle;
+ ctx.lineWidth = valueOrDefault$1(vm.borderWidth, globalOptionLineElements.borderWidth);
+ ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor;
+
+ // Stroke Line
+ ctx.beginPath();
+
+ // First point moves to it's starting position no matter what
+ currentVM = points[0]._view;
+ if (!currentVM.skip) {
+ ctx.moveTo(currentVM.x, currentVM.y);
+ lastDrawnIndex = 0;
+ }
+
+ for (index = 1; index < points.length; ++index) {
+ currentVM = points[index]._view;
+ previous = lastDrawnIndex === -1 ? helpers$1.previousItem(points, index) : points[lastDrawnIndex];
+
+ if (!currentVM.skip) {
+ if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) {
+ // There was a gap and this is the first point after the gap
+ ctx.moveTo(currentVM.x, currentVM.y);
+ } else {
+ // Line to next point
+ helpers$1.canvas.lineTo(ctx, previous._view, currentVM);
+ }
+ lastDrawnIndex = index;
+ }
+ }
+
+ if (closePath) {
+ ctx.closePath();
+ }
+
+ ctx.stroke();
+ ctx.restore();
+ }
+});
+
+var valueOrDefault$2 = helpers$1.valueOrDefault;
+
+var defaultColor$1 = core_defaults.global.defaultColor;
+
+core_defaults._set('global', {
+ elements: {
+ point: {
+ radius: 3,
+ pointStyle: 'circle',
+ backgroundColor: defaultColor$1,
+ borderColor: defaultColor$1,
+ borderWidth: 1,
+ // Hover
+ hitRadius: 1,
+ hoverRadius: 4,
+ hoverBorderWidth: 1
+ }
+ }
+});
+
+function xRange(mouseX) {
+ var vm = this._view;
+ return vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false;
+}
+
+function yRange(mouseY) {
+ var vm = this._view;
+ return vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false;
+}
+
+var element_point = core_element.extend({
+ _type: 'point',
+
+ inRange: function(mouseX, mouseY) {
+ var vm = this._view;
+ return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false;
+ },
+
+ inLabelRange: xRange,
+ inXRange: xRange,
+ inYRange: yRange,
+
+ getCenterPoint: function() {
+ var vm = this._view;
+ return {
+ x: vm.x,
+ y: vm.y
+ };
+ },
+
+ getArea: function() {
+ return Math.PI * Math.pow(this._view.radius, 2);
+ },
+
+ tooltipPosition: function() {
+ var vm = this._view;
+ return {
+ x: vm.x,
+ y: vm.y,
+ padding: vm.radius + vm.borderWidth
+ };
+ },
+
+ draw: function(chartArea) {
+ var vm = this._view;
+ var ctx = this._chart.ctx;
+ var pointStyle = vm.pointStyle;
+ var rotation = vm.rotation;
+ var radius = vm.radius;
+ var x = vm.x;
+ var y = vm.y;
+ var globalDefaults = core_defaults.global;
+ var defaultColor = globalDefaults.defaultColor; // eslint-disable-line no-shadow
+
+ if (vm.skip) {
+ return;
+ }
+
+ // Clipping for Points.
+ if (chartArea === undefined || helpers$1.canvas._isPointInArea(vm, chartArea)) {
+ ctx.strokeStyle = vm.borderColor || defaultColor;
+ ctx.lineWidth = valueOrDefault$2(vm.borderWidth, globalDefaults.elements.point.borderWidth);
+ ctx.fillStyle = vm.backgroundColor || defaultColor;
+ helpers$1.canvas.drawPoint(ctx, pointStyle, radius, x, y, rotation);
+ }
+ }
+});
+
+var defaultColor$2 = core_defaults.global.defaultColor;
+
+core_defaults._set('global', {
+ elements: {
+ rectangle: {
+ backgroundColor: defaultColor$2,
+ borderColor: defaultColor$2,
+ borderSkipped: 'bottom',
+ borderWidth: 0
+ }
+ }
+});
+
+function isVertical(vm) {
+ return vm && vm.width !== undefined;
+}
+
+/**
+ * Helper function to get the bounds of the bar regardless of the orientation
+ * @param bar {Chart.Element.Rectangle} the bar
+ * @return {Bounds} bounds of the bar
+ * @private
+ */
+function getBarBounds(vm) {
+ var x1, x2, y1, y2, half;
+
+ if (isVertical(vm)) {
+ half = vm.width / 2;
+ x1 = vm.x - half;
+ x2 = vm.x + half;
+ y1 = Math.min(vm.y, vm.base);
+ y2 = Math.max(vm.y, vm.base);
+ } else {
+ half = vm.height / 2;
+ x1 = Math.min(vm.x, vm.base);
+ x2 = Math.max(vm.x, vm.base);
+ y1 = vm.y - half;
+ y2 = vm.y + half;
+ }
+
+ return {
+ left: x1,
+ top: y1,
+ right: x2,
+ bottom: y2
+ };
+}
+
+function swap(orig, v1, v2) {
+ return orig === v1 ? v2 : orig === v2 ? v1 : orig;
+}
+
+function parseBorderSkipped(vm) {
+ var edge = vm.borderSkipped;
+ var res = {};
+
+ if (!edge) {
+ return res;
+ }
+
+ if (vm.horizontal) {
+ if (vm.base > vm.x) {
+ edge = swap(edge, 'left', 'right');
+ }
+ } else if (vm.base < vm.y) {
+ edge = swap(edge, 'bottom', 'top');
+ }
+
+ res[edge] = true;
+ return res;
+}
+
+function parseBorderWidth(vm, maxW, maxH) {
+ var value = vm.borderWidth;
+ var skip = parseBorderSkipped(vm);
+ var t, r, b, l;
+
+ if (helpers$1.isObject(value)) {
+ t = +value.top || 0;
+ r = +value.right || 0;
+ b = +value.bottom || 0;
+ l = +value.left || 0;
+ } else {
+ t = r = b = l = +value || 0;
+ }
+
+ return {
+ t: skip.top || (t < 0) ? 0 : t > maxH ? maxH : t,
+ r: skip.right || (r < 0) ? 0 : r > maxW ? maxW : r,
+ b: skip.bottom || (b < 0) ? 0 : b > maxH ? maxH : b,
+ l: skip.left || (l < 0) ? 0 : l > maxW ? maxW : l
+ };
+}
+
+function boundingRects(vm) {
+ var bounds = getBarBounds(vm);
+ var width = bounds.right - bounds.left;
+ var height = bounds.bottom - bounds.top;
+ var border = parseBorderWidth(vm, width / 2, height / 2);
+
+ return {
+ outer: {
+ x: bounds.left,
+ y: bounds.top,
+ w: width,
+ h: height
+ },
+ inner: {
+ x: bounds.left + border.l,
+ y: bounds.top + border.t,
+ w: width - border.l - border.r,
+ h: height - border.t - border.b
+ }
+ };
+}
+
+function inRange(vm, x, y) {
+ var skipX = x === null;
+ var skipY = y === null;
+ var bounds = !vm || (skipX && skipY) ? false : getBarBounds(vm);
+
+ return bounds
+ && (skipX || x >= bounds.left && x <= bounds.right)
+ && (skipY || y >= bounds.top && y <= bounds.bottom);
+}
+
+var element_rectangle = core_element.extend({
+ _type: 'rectangle',
+
+ draw: function() {
+ var ctx = this._chart.ctx;
+ var vm = this._view;
+ var rects = boundingRects(vm);
+ var outer = rects.outer;
+ var inner = rects.inner;
+
+ ctx.save();
+
+ if (outer.w !== inner.w || outer.h !== inner.h) {
+ ctx.beginPath();
+ ctx.strokeStyle = vm.borderColor;
+ ctx.strokeRect(inner.x, inner.y, inner.w, inner.h);
+ ctx.fill('evenodd');
+ }
+
+ ctx.fillStyle = vm.backgroundColor;
+ ctx.fillRect(inner.x, inner.y, inner.w, inner.h);
+
+ ctx.restore();
+ },
+
+ height: function() {
+ var vm = this._view;
+ return vm.base - vm.y;
+ },
+
+ inRange: function(mouseX, mouseY) {
+ return inRange(this._view, mouseX, mouseY);
+ },
+
+ inLabelRange: function(mouseX, mouseY) {
+ var vm = this._view;
+ return isVertical(vm)
+ ? inRange(vm, mouseX, null)
+ : inRange(vm, null, mouseY);
+ },
+
+ inXRange: function(mouseX) {
+ return inRange(this._view, mouseX, null);
+ },
+
+ inYRange: function(mouseY) {
+ return inRange(this._view, null, mouseY);
+ },
+
+ getCenterPoint: function() {
+ var vm = this._view;
+ var x, y;
+ if (isVertical(vm)) {
+ x = vm.x;
+ y = (vm.y + vm.base) / 2;
+ } else {
+ x = (vm.x + vm.base) / 2;
+ y = vm.y;
+ }
+
+ return {x: x, y: y};
+ },
+
+ getArea: function() {
+ var vm = this._view;
+
+ return isVertical(vm)
+ ? vm.width * Math.abs(vm.y - vm.base)
+ : vm.height * Math.abs(vm.x - vm.base);
+ },
+
+ tooltipPosition: function() {
+ var vm = this._view;
+ return {
+ x: vm.x,
+ y: vm.y
+ };
+ }
+});
+
+var elements = {};
+var Arc = element_arc;
+var Line = element_line;
+var Point = element_point;
+var Rectangle = element_rectangle;
+elements.Arc = Arc;
+elements.Line = Line;
+elements.Point = Point;
+elements.Rectangle = Rectangle;
+
+var deprecated = helpers$1._deprecated;
+var valueOrDefault$3 = helpers$1.valueOrDefault;
+
+core_defaults._set('bar', {
+ hover: {
+ mode: 'label'
+ },
+
+ scales: {
+ xAxes: [{
+ type: 'category',
+ offset: true,
+ gridLines: {
+ offsetGridLines: true
+ }
+ }],
+
+ yAxes: [{
+ type: 'linear'
+ }]
+ }
+});
+
+core_defaults._set('global', {
+ datasets: {
+ bar: {
+ categoryPercentage: 0.8,
+ barPercentage: 0.9
+ }
+ }
+});
+
+/**
+ * Computes the "optimal" sample size to maintain bars equally sized while preventing overlap.
+ * @private
+ */
+function computeMinSampleSize(scale, pixels) {
+ var min = scale._length;
+ var prev, curr, i, ilen;
+
+ for (i = 1, ilen = pixels.length; i < ilen; ++i) {
+ min = Math.min(min, Math.abs(pixels[i] - pixels[i - 1]));
+ }
+
+ for (i = 0, ilen = scale.getTicks().length; i < ilen; ++i) {
+ curr = scale.getPixelForTick(i);
+ min = i > 0 ? Math.min(min, Math.abs(curr - prev)) : min;
+ prev = curr;
+ }
+
+ return min;
+}
+
+/**
+ * Computes an "ideal" category based on the absolute bar thickness or, if undefined or null,
+ * uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This
+ * mode currently always generates bars equally sized (until we introduce scriptable options?).
+ * @private
+ */
+function computeFitCategoryTraits(index, ruler, options) {
+ var thickness = options.barThickness;
+ var count = ruler.stackCount;
+ var curr = ruler.pixels[index];
+ var min = helpers$1.isNullOrUndef(thickness)
+ ? computeMinSampleSize(ruler.scale, ruler.pixels)
+ : -1;
+ var size, ratio;
+
+ if (helpers$1.isNullOrUndef(thickness)) {
+ size = min * options.categoryPercentage;
+ ratio = options.barPercentage;
+ } else {
+ // When bar thickness is enforced, category and bar percentages are ignored.
+ // Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%')
+ // and deprecate barPercentage since this value is ignored when thickness is absolute.
+ size = thickness * count;
+ ratio = 1;
+ }
+
+ return {
+ chunk: size / count,
+ ratio: ratio,
+ start: curr - (size / 2)
+ };
+}
+
+/**
+ * Computes an "optimal" category that globally arranges bars side by side (no gap when
+ * percentage options are 1), based on the previous and following categories. This mode
+ * generates bars with different widths when data are not evenly spaced.
+ * @private
+ */
+function computeFlexCategoryTraits(index, ruler, options) {
+ var pixels = ruler.pixels;
+ var curr = pixels[index];
+ var prev = index > 0 ? pixels[index - 1] : null;
+ var next = index < pixels.length - 1 ? pixels[index + 1] : null;
+ var percent = options.categoryPercentage;
+ var start, size;
+
+ if (prev === null) {
+ // first data: its size is double based on the next point or,
+ // if it's also the last data, we use the scale size.
+ prev = curr - (next === null ? ruler.end - ruler.start : next - curr);
+ }
+
+ if (next === null) {
+ // last data: its size is also double based on the previous point.
+ next = curr + curr - prev;
+ }
+
+ start = curr - (curr - Math.min(prev, next)) / 2 * percent;
+ size = Math.abs(next - prev) / 2 * percent;
+
+ return {
+ chunk: size / ruler.stackCount,
+ ratio: options.barPercentage,
+ start: start
+ };
+}
+
+var controller_bar = core_datasetController.extend({
+
+ dataElementType: elements.Rectangle,
+
+ /**
+ * @private
+ */
+ _dataElementOptions: [
+ 'backgroundColor',
+ 'borderColor',
+ 'borderSkipped',
+ 'borderWidth',
+ 'barPercentage',
+ 'barThickness',
+ 'categoryPercentage',
+ 'maxBarThickness',
+ 'minBarLength'
+ ],
+
+ initialize: function() {
+ var me = this;
+ var meta, scaleOpts;
+
+ core_datasetController.prototype.initialize.apply(me, arguments);
+
+ meta = me.getMeta();
+ meta.stack = me.getDataset().stack;
+ meta.bar = true;
+
+ scaleOpts = me._getIndexScale().options;
+ deprecated('bar chart', scaleOpts.barPercentage, 'scales.[x/y]Axes.barPercentage', 'dataset.barPercentage');
+ deprecated('bar chart', scaleOpts.barThickness, 'scales.[x/y]Axes.barThickness', 'dataset.barThickness');
+ deprecated('bar chart', scaleOpts.categoryPercentage, 'scales.[x/y]Axes.categoryPercentage', 'dataset.categoryPercentage');
+ deprecated('bar chart', me._getValueScale().options.minBarLength, 'scales.[x/y]Axes.minBarLength', 'dataset.minBarLength');
+ deprecated('bar chart', scaleOpts.maxBarThickness, 'scales.[x/y]Axes.maxBarThickness', 'dataset.maxBarThickness');
+ },
+
+ update: function(reset) {
+ var me = this;
+ var rects = me.getMeta().data;
+ var i, ilen;
+
+ me._ruler = me.getRuler();
+
+ for (i = 0, ilen = rects.length; i < ilen; ++i) {
+ me.updateElement(rects[i], i, reset);
+ }
+ },
+
+ updateElement: function(rectangle, index, reset) {
+ var me = this;
+ var meta = me.getMeta();
+ var dataset = me.getDataset();
+ var options = me._resolveDataElementOptions(rectangle, index);
+
+ rectangle._xScale = me.getScaleForId(meta.xAxisID);
+ rectangle._yScale = me.getScaleForId(meta.yAxisID);
+ rectangle._datasetIndex = me.index;
+ rectangle._index = index;
+ rectangle._model = {
+ backgroundColor: options.backgroundColor,
+ borderColor: options.borderColor,
+ borderSkipped: options.borderSkipped,
+ borderWidth: options.borderWidth,
+ datasetLabel: dataset.label,
+ label: me.chart.data.labels[index]
+ };
+
+ if (helpers$1.isArray(dataset.data[index])) {
+ rectangle._model.borderSkipped = null;
+ }
+
+ me._updateElementGeometry(rectangle, index, reset, options);
+
+ rectangle.pivot();
+ },
+
+ /**
+ * @private
+ */
+ _updateElementGeometry: function(rectangle, index, reset, options) {
+ var me = this;
+ var model = rectangle._model;
+ var vscale = me._getValueScale();
+ var base = vscale.getBasePixel();
+ var horizontal = vscale.isHorizontal();
+ var ruler = me._ruler || me.getRuler();
+ var vpixels = me.calculateBarValuePixels(me.index, index, options);
+ var ipixels = me.calculateBarIndexPixels(me.index, index, ruler, options);
+
+ model.horizontal = horizontal;
+ model.base = reset ? base : vpixels.base;
+ model.x = horizontal ? reset ? base : vpixels.head : ipixels.center;
+ model.y = horizontal ? ipixels.center : reset ? base : vpixels.head;
+ model.height = horizontal ? ipixels.size : undefined;
+ model.width = horizontal ? undefined : ipixels.size;
+ },
+
+ /**
+ * Returns the stacks based on groups and bar visibility.
+ * @param {number} [last] - The dataset index
+ * @returns {string[]} The list of stack IDs
+ * @private
+ */
+ _getStacks: function(last) {
+ var me = this;
+ var scale = me._getIndexScale();
+ var metasets = scale._getMatchingVisibleMetas(me._type);
+ var stacked = scale.options.stacked;
+ var ilen = metasets.length;
+ var stacks = [];
+ var i, meta;
+
+ for (i = 0; i < ilen; ++i) {
+ meta = metasets[i];
+ // stacked | meta.stack
+ // | found | not found | undefined
+ // false | x | x | x
+ // true | | x |
+ // undefined | | x | x
+ if (stacked === false || stacks.indexOf(meta.stack) === -1 ||
+ (stacked === undefined && meta.stack === undefined)) {
+ stacks.push(meta.stack);
+ }
+ if (meta.index === last) {
+ break;
+ }
+ }
+
+ return stacks;
+ },
+
+ /**
+ * Returns the effective number of stacks based on groups and bar visibility.
+ * @private
+ */
+ getStackCount: function() {
+ return this._getStacks().length;
+ },
+
+ /**
+ * Returns the stack index for the given dataset based on groups and bar visibility.
+ * @param {number} [datasetIndex] - The dataset index
+ * @param {string} [name] - The stack name to find
+ * @returns {number} The stack index
+ * @private
+ */
+ getStackIndex: function(datasetIndex, name) {
+ var stacks = this._getStacks(datasetIndex);
+ var index = (name !== undefined)
+ ? stacks.indexOf(name)
+ : -1; // indexOf returns -1 if element is not present
+
+ return (index === -1)
+ ? stacks.length - 1
+ : index;
+ },
+
+ /**
+ * @private
+ */
+ getRuler: function() {
+ var me = this;
+ var scale = me._getIndexScale();
+ var pixels = [];
+ var i, ilen;
+
+ for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) {
+ pixels.push(scale.getPixelForValue(null, i, me.index));
+ }
+
+ return {
+ pixels: pixels,
+ start: scale._startPixel,
+ end: scale._endPixel,
+ stackCount: me.getStackCount(),
+ scale: scale
+ };
+ },
+
+ /**
+ * Note: pixel values are not clamped to the scale area.
+ * @private
+ */
+ calculateBarValuePixels: function(datasetIndex, index, options) {
+ var me = this;
+ var chart = me.chart;
+ var scale = me._getValueScale();
+ var isHorizontal = scale.isHorizontal();
+ var datasets = chart.data.datasets;
+ var metasets = scale._getMatchingVisibleMetas(me._type);
+ var value = scale._parseValue(datasets[datasetIndex].data[index]);
+ var minBarLength = options.minBarLength;
+ var stacked = scale.options.stacked;
+ var stack = me.getMeta().stack;
+ var start = value.start === undefined ? 0 : value.max >= 0 && value.min >= 0 ? value.min : value.max;
+ var length = value.start === undefined ? value.end : value.max >= 0 && value.min >= 0 ? value.max - value.min : value.min - value.max;
+ var ilen = metasets.length;
+ var i, imeta, ivalue, base, head, size, stackLength;
+
+ if (stacked || (stacked === undefined && stack !== undefined)) {
+ for (i = 0; i < ilen; ++i) {
+ imeta = metasets[i];
+
+ if (imeta.index === datasetIndex) {
+ break;
+ }
+
+ if (imeta.stack === stack) {
+ stackLength = scale._parseValue(datasets[imeta.index].data[index]);
+ ivalue = stackLength.start === undefined ? stackLength.end : stackLength.min >= 0 && stackLength.max >= 0 ? stackLength.max : stackLength.min;
+
+ if ((value.min < 0 && ivalue < 0) || (value.max >= 0 && ivalue > 0)) {
+ start += ivalue;
+ }
+ }
+ }
+ }
+
+ base = scale.getPixelForValue(start);
+ head = scale.getPixelForValue(start + length);
+ size = head - base;
+
+ if (minBarLength !== undefined && Math.abs(size) < minBarLength) {
+ size = minBarLength;
+ if (length >= 0 && !isHorizontal || length < 0 && isHorizontal) {
+ head = base - minBarLength;
+ } else {
+ head = base + minBarLength;
+ }
+ }
+
+ return {
+ size: size,
+ base: base,
+ head: head,
+ center: head + size / 2
+ };
+ },
+
+ /**
+ * @private
+ */
+ calculateBarIndexPixels: function(datasetIndex, index, ruler, options) {
+ var me = this;
+ var range = options.barThickness === 'flex'
+ ? computeFlexCategoryTraits(index, ruler, options)
+ : computeFitCategoryTraits(index, ruler, options);
+
+ var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack);
+ var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2);
+ var size = Math.min(
+ valueOrDefault$3(options.maxBarThickness, Infinity),
+ range.chunk * range.ratio);
+
+ return {
+ base: center - size / 2,
+ head: center + size / 2,
+ center: center,
+ size: size
+ };
+ },
+
+ draw: function() {
+ var me = this;
+ var chart = me.chart;
+ var scale = me._getValueScale();
+ var rects = me.getMeta().data;
+ var dataset = me.getDataset();
+ var ilen = rects.length;
+ var i = 0;
+
+ helpers$1.canvas.clipArea(chart.ctx, chart.chartArea);
+
+ for (; i < ilen; ++i) {
+ var val = scale._parseValue(dataset.data[i]);
+ if (!isNaN(val.min) && !isNaN(val.max)) {
+ rects[i].draw();
+ }
+ }
+
+ helpers$1.canvas.unclipArea(chart.ctx);
+ },
+
+ /**
+ * @private
+ */
+ _resolveDataElementOptions: function() {
+ var me = this;
+ var values = helpers$1.extend({}, core_datasetController.prototype._resolveDataElementOptions.apply(me, arguments));
+ var indexOpts = me._getIndexScale().options;
+ var valueOpts = me._getValueScale().options;
+
+ values.barPercentage = valueOrDefault$3(indexOpts.barPercentage, values.barPercentage);
+ values.barThickness = valueOrDefault$3(indexOpts.barThickness, values.barThickness);
+ values.categoryPercentage = valueOrDefault$3(indexOpts.categoryPercentage, values.categoryPercentage);
+ values.maxBarThickness = valueOrDefault$3(indexOpts.maxBarThickness, values.maxBarThickness);
+ values.minBarLength = valueOrDefault$3(valueOpts.minBarLength, values.minBarLength);
+
+ return values;
+ }
+
+});
+
+var valueOrDefault$4 = helpers$1.valueOrDefault;
+var resolve$1 = helpers$1.options.resolve;
+
+core_defaults._set('bubble', {
+ hover: {
+ mode: 'single'
+ },
+
+ scales: {
+ xAxes: [{
+ type: 'linear', // bubble should probably use a linear scale by default
+ position: 'bottom',
+ id: 'x-axis-0' // need an ID so datasets can reference the scale
+ }],
+ yAxes: [{
+ type: 'linear',
+ position: 'left',
+ id: 'y-axis-0'
+ }]
+ },
+
+ tooltips: {
+ callbacks: {
+ title: function() {
+ // Title doesn't make sense for scatter since we format the data as a point
+ return '';
+ },
+ label: function(item, data) {
+ var datasetLabel = data.datasets[item.datasetIndex].label || '';
+ var dataPoint = data.datasets[item.datasetIndex].data[item.index];
+ return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')';
+ }
+ }
+ }
+});
+
+var controller_bubble = core_datasetController.extend({
+ /**
+ * @protected
+ */
+ dataElementType: elements.Point,
+
+ /**
+ * @private
+ */
+ _dataElementOptions: [
+ 'backgroundColor',
+ 'borderColor',
+ 'borderWidth',
+ 'hoverBackgroundColor',
+ 'hoverBorderColor',
+ 'hoverBorderWidth',
+ 'hoverRadius',
+ 'hitRadius',
+ 'pointStyle',
+ 'rotation'
+ ],
+
+ /**
+ * @protected
+ */
+ update: function(reset) {
+ var me = this;
+ var meta = me.getMeta();
+ var points = meta.data;
+
+ // Update Points
+ helpers$1.each(points, function(point, index) {
+ me.updateElement(point, index, reset);
+ });
+ },
+
+ /**
+ * @protected
+ */
+ updateElement: function(point, index, reset) {
+ var me = this;
+ var meta = me.getMeta();
+ var custom = point.custom || {};
+ var xScale = me.getScaleForId(meta.xAxisID);
+ var yScale = me.getScaleForId(meta.yAxisID);
+ var options = me._resolveDataElementOptions(point, index);
+ var data = me.getDataset().data[index];
+ var dsIndex = me.index;
+
+ var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex);
+ var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex);
+
+ point._xScale = xScale;
+ point._yScale = yScale;
+ point._options = options;
+ point._datasetIndex = dsIndex;
+ point._index = index;
+ point._model = {
+ backgroundColor: options.backgroundColor,
+ borderColor: options.borderColor,
+ borderWidth: options.borderWidth,
+ hitRadius: options.hitRadius,
+ pointStyle: options.pointStyle,
+ rotation: options.rotation,
+ radius: reset ? 0 : options.radius,
+ skip: custom.skip || isNaN(x) || isNaN(y),
+ x: x,
+ y: y,
+ };
+
+ point.pivot();
+ },
+
+ /**
+ * @protected
+ */
+ setHoverStyle: function(point) {
+ var model = point._model;
+ var options = point._options;
+ var getHoverColor = helpers$1.getHoverColor;
+
+ point.$previousStyle = {
+ backgroundColor: model.backgroundColor,
+ borderColor: model.borderColor,
+ borderWidth: model.borderWidth,
+ radius: model.radius
+ };
+
+ model.backgroundColor = valueOrDefault$4(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
+ model.borderColor = valueOrDefault$4(options.hoverBorderColor, getHoverColor(options.borderColor));
+ model.borderWidth = valueOrDefault$4(options.hoverBorderWidth, options.borderWidth);
+ model.radius = options.radius + options.hoverRadius;
+ },
+
+ /**
+ * @private
+ */
+ _resolveDataElementOptions: function(point, index) {
+ var me = this;
+ var chart = me.chart;
+ var dataset = me.getDataset();
+ var custom = point.custom || {};
+ var data = dataset.data[index] || {};
+ var values = core_datasetController.prototype._resolveDataElementOptions.apply(me, arguments);
+
+ // Scriptable options
+ var context = {
+ chart: chart,
+ dataIndex: index,
+ dataset: dataset,
+ datasetIndex: me.index
+ };
+
+ // In case values were cached (and thus frozen), we need to clone the values
+ if (me._cachedDataOpts === values) {
+ values = helpers$1.extend({}, values);
+ }
+
+ // Custom radius resolution
+ values.radius = resolve$1([
+ custom.radius,
+ data.r,
+ me._config.radius,
+ chart.options.elements.point.radius
+ ], context, index);
+
+ return values;
+ }
+});
+
+var valueOrDefault$5 = helpers$1.valueOrDefault;
+
+var PI$1 = Math.PI;
+var DOUBLE_PI$1 = PI$1 * 2;
+var HALF_PI$1 = PI$1 / 2;
+
+core_defaults._set('doughnut', {
+ animation: {
+ // Boolean - Whether we animate the rotation of the Doughnut
+ animateRotate: true,
+ // Boolean - Whether we animate scaling the Doughnut from the centre
+ animateScale: false
+ },
+ hover: {
+ mode: 'single'
+ },
+ legendCallback: function(chart) {
+ var list = document.createElement('ul');
+ var data = chart.data;
+ var datasets = data.datasets;
+ var labels = data.labels;
+ var i, ilen, listItem, listItemSpan;
+
+ list.setAttribute('class', chart.id + '-legend');
+ if (datasets.length) {
+ for (i = 0, ilen = datasets[0].data.length; i < ilen; ++i) {
+ listItem = list.appendChild(document.createElement('li'));
+ listItemSpan = listItem.appendChild(document.createElement('span'));
+ listItemSpan.style.backgroundColor = datasets[0].backgroundColor[i];
+ if (labels[i]) {
+ listItem.appendChild(document.createTextNode(labels[i]));
+ }
+ }
+ }
+
+ return list.outerHTML;
+ },
+ legend: {
+ labels: {
+ generateLabels: function(chart) {
+ var data = chart.data;
+ if (data.labels.length && data.datasets.length) {
+ return data.labels.map(function(label, i) {
+ var meta = chart.getDatasetMeta(0);
+ var style = meta.controller.getStyle(i);
+
+ return {
+ text: label,
+ fillStyle: style.backgroundColor,
+ strokeStyle: style.borderColor,
+ lineWidth: style.borderWidth,
+ hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden,
+
+ // Extra data used for toggling the correct item
+ index: i
+ };
+ });
+ }
+ return [];
+ }
+ },
+
+ onClick: function(e, legendItem) {
+ var index = legendItem.index;
+ var chart = this.chart;
+ var i, ilen, meta;
+
+ for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
+ meta = chart.getDatasetMeta(i);
+ // toggle visibility of index if exists
+ if (meta.data[index]) {
+ meta.data[index].hidden = !meta.data[index].hidden;
+ }
+ }
+
+ chart.update();
+ }
+ },
+
+ // The percentage of the chart that we cut out of the middle.
+ cutoutPercentage: 50,
+
+ // The rotation of the chart, where the first data arc begins.
+ rotation: -HALF_PI$1,
+
+ // The total circumference of the chart.
+ circumference: DOUBLE_PI$1,
+
+ // Need to override these to give a nice default
+ tooltips: {
+ callbacks: {
+ title: function() {
+ return '';
+ },
+ label: function(tooltipItem, data) {
+ var dataLabel = data.labels[tooltipItem.index];
+ var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
+
+ if (helpers$1.isArray(dataLabel)) {
+ // show value on first line of multiline label
+ // need to clone because we are changing the value
+ dataLabel = dataLabel.slice();
+ dataLabel[0] += value;
+ } else {
+ dataLabel += value;
+ }
+
+ return dataLabel;
+ }
+ }
+ }
+});
+
+var controller_doughnut = core_datasetController.extend({
+
+ dataElementType: elements.Arc,
+
+ linkScales: helpers$1.noop,
+
+ /**
+ * @private
+ */
+ _dataElementOptions: [
+ 'backgroundColor',
+ 'borderColor',
+ 'borderWidth',
+ 'borderAlign',
+ 'hoverBackgroundColor',
+ 'hoverBorderColor',
+ 'hoverBorderWidth',
+ ],
+
+ // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly
+ getRingIndex: function(datasetIndex) {
+ var ringIndex = 0;
+
+ for (var j = 0; j < datasetIndex; ++j) {
+ if (this.chart.isDatasetVisible(j)) {
+ ++ringIndex;
+ }
+ }
+
+ return ringIndex;
+ },
+
+ update: function(reset) {
+ var me = this;
+ var chart = me.chart;
+ var chartArea = chart.chartArea;
+ var opts = chart.options;
+ var ratioX = 1;
+ var ratioY = 1;
+ var offsetX = 0;
+ var offsetY = 0;
+ var meta = me.getMeta();
+ var arcs = meta.data;
+ var cutout = opts.cutoutPercentage / 100 || 0;
+ var circumference = opts.circumference;
+ var chartWeight = me._getRingWeight(me.index);
+ var maxWidth, maxHeight, i, ilen;
+
+ // If the chart's circumference isn't a full circle, calculate size as a ratio of the width/height of the arc
+ if (circumference < DOUBLE_PI$1) {
+ var startAngle = opts.rotation % DOUBLE_PI$1;
+ startAngle += startAngle >= PI$1 ? -DOUBLE_PI$1 : startAngle < -PI$1 ? DOUBLE_PI$1 : 0;
+ var endAngle = startAngle + circumference;
+ var startX = Math.cos(startAngle);
+ var startY = Math.sin(startAngle);
+ var endX = Math.cos(endAngle);
+ var endY = Math.sin(endAngle);
+ var contains0 = (startAngle <= 0 && endAngle >= 0) || endAngle >= DOUBLE_PI$1;
+ var contains90 = (startAngle <= HALF_PI$1 && endAngle >= HALF_PI$1) || endAngle >= DOUBLE_PI$1 + HALF_PI$1;
+ var contains180 = startAngle === -PI$1 || endAngle >= PI$1;
+ var contains270 = (startAngle <= -HALF_PI$1 && endAngle >= -HALF_PI$1) || endAngle >= PI$1 + HALF_PI$1;
+ var minX = contains180 ? -1 : Math.min(startX, startX * cutout, endX, endX * cutout);
+ var minY = contains270 ? -1 : Math.min(startY, startY * cutout, endY, endY * cutout);
+ var maxX = contains0 ? 1 : Math.max(startX, startX * cutout, endX, endX * cutout);
+ var maxY = contains90 ? 1 : Math.max(startY, startY * cutout, endY, endY * cutout);
+ ratioX = (maxX - minX) / 2;
+ ratioY = (maxY - minY) / 2;
+ offsetX = -(maxX + minX) / 2;
+ offsetY = -(maxY + minY) / 2;
+ }
+
+ for (i = 0, ilen = arcs.length; i < ilen; ++i) {
+ arcs[i]._options = me._resolveDataElementOptions(arcs[i], i);
+ }
+
+ chart.borderWidth = me.getMaxBorderWidth();
+ maxWidth = (chartArea.right - chartArea.left - chart.borderWidth) / ratioX;
+ maxHeight = (chartArea.bottom - chartArea.top - chart.borderWidth) / ratioY;
+ chart.outerRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0);
+ chart.innerRadius = Math.max(chart.outerRadius * cutout, 0);
+ chart.radiusLength = (chart.outerRadius - chart.innerRadius) / (me._getVisibleDatasetWeightTotal() || 1);
+ chart.offsetX = offsetX * chart.outerRadius;
+ chart.offsetY = offsetY * chart.outerRadius;
+
+ meta.total = me.calculateTotal();
+
+ me.outerRadius = chart.outerRadius - chart.radiusLength * me._getRingWeightOffset(me.index);
+ me.innerRadius = Math.max(me.outerRadius - chart.radiusLength * chartWeight, 0);
+
+ for (i = 0, ilen = arcs.length; i < ilen; ++i) {
+ me.updateElement(arcs[i], i, reset);
+ }
+ },
+
+ updateElement: function(arc, index, reset) {
+ var me = this;
+ var chart = me.chart;
+ var chartArea = chart.chartArea;
+ var opts = chart.options;
+ var animationOpts = opts.animation;
+ var centerX = (chartArea.left + chartArea.right) / 2;
+ var centerY = (chartArea.top + chartArea.bottom) / 2;
+ var startAngle = opts.rotation; // non reset case handled later
+ var endAngle = opts.rotation; // non reset case handled later
+ var dataset = me.getDataset();
+ var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / DOUBLE_PI$1);
+ var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;
+ var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;
+ var options = arc._options || {};
+
+ helpers$1.extend(arc, {
+ // Utility
+ _datasetIndex: me.index,
+ _index: index,
+
+ // Desired view properties
+ _model: {
+ backgroundColor: options.backgroundColor,
+ borderColor: options.borderColor,
+ borderWidth: options.borderWidth,
+ borderAlign: options.borderAlign,
+ x: centerX + chart.offsetX,
+ y: centerY + chart.offsetY,
+ startAngle: startAngle,
+ endAngle: endAngle,
+ circumference: circumference,
+ outerRadius: outerRadius,
+ innerRadius: innerRadius,
+ label: helpers$1.valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
+ }
+ });
+
+ var model = arc._model;
+
+ // Set correct angles if not resetting
+ if (!reset || !animationOpts.animateRotate) {
+ if (index === 0) {
+ model.startAngle = opts.rotation;
+ } else {
+ model.startAngle = me.getMeta().data[index - 1]._model.endAngle;
+ }
+
+ model.endAngle = model.startAngle + model.circumference;
+ }
+
+ arc.pivot();
+ },
+
+ calculateTotal: function() {
+ var dataset = this.getDataset();
+ var meta = this.getMeta();
+ var total = 0;
+ var value;
+
+ helpers$1.each(meta.data, function(element, index) {
+ value = dataset.data[index];
+ if (!isNaN(value) && !element.hidden) {
+ total += Math.abs(value);
+ }
+ });
+
+ /* if (total === 0) {
+ total = NaN;
+ }*/
+
+ return total;
+ },
+
+ calculateCircumference: function(value) {
+ var total = this.getMeta().total;
+ if (total > 0 && !isNaN(value)) {
+ return DOUBLE_PI$1 * (Math.abs(value) / total);
+ }
+ return 0;
+ },
+
+ // gets the max border or hover width to properly scale pie charts
+ getMaxBorderWidth: function(arcs) {
+ var me = this;
+ var max = 0;
+ var chart = me.chart;
+ var i, ilen, meta, arc, controller, options, borderWidth, hoverWidth;
+
+ if (!arcs) {
+ // Find the outmost visible dataset
+ for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
+ if (chart.isDatasetVisible(i)) {
+ meta = chart.getDatasetMeta(i);
+ arcs = meta.data;
+ if (i !== me.index) {
+ controller = meta.controller;
+ }
+ break;
+ }
+ }
+ }
+
+ if (!arcs) {
+ return 0;
+ }
+
+ for (i = 0, ilen = arcs.length; i < ilen; ++i) {
+ arc = arcs[i];
+ if (controller) {
+ controller._configure();
+ options = controller._resolveDataElementOptions(arc, i);
+ } else {
+ options = arc._options;
+ }
+ if (options.borderAlign !== 'inner') {
+ borderWidth = options.borderWidth;
+ hoverWidth = options.hoverBorderWidth;
+
+ max = borderWidth > max ? borderWidth : max;
+ max = hoverWidth > max ? hoverWidth : max;
+ }
+ }
+ return max;
+ },
+
+ /**
+ * @protected
+ */
+ setHoverStyle: function(arc) {
+ var model = arc._model;
+ var options = arc._options;
+ var getHoverColor = helpers$1.getHoverColor;
+
+ arc.$previousStyle = {
+ backgroundColor: model.backgroundColor,
+ borderColor: model.borderColor,
+ borderWidth: model.borderWidth,
+ };
+
+ model.backgroundColor = valueOrDefault$5(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
+ model.borderColor = valueOrDefault$5(options.hoverBorderColor, getHoverColor(options.borderColor));
+ model.borderWidth = valueOrDefault$5(options.hoverBorderWidth, options.borderWidth);
+ },
+
+ /**
+ * Get radius length offset of the dataset in relation to the visible datasets weights. This allows determining the inner and outer radius correctly
+ * @private
+ */
+ _getRingWeightOffset: function(datasetIndex) {
+ var ringWeightOffset = 0;
+
+ for (var i = 0; i < datasetIndex; ++i) {
+ if (this.chart.isDatasetVisible(i)) {
+ ringWeightOffset += this._getRingWeight(i);
+ }
+ }
+
+ return ringWeightOffset;
+ },
+
+ /**
+ * @private
+ */
+ _getRingWeight: function(dataSetIndex) {
+ return Math.max(valueOrDefault$5(this.chart.data.datasets[dataSetIndex].weight, 1), 0);
+ },
+
+ /**
+ * Returns the sum of all visibile data set weights. This value can be 0.
+ * @private
+ */
+ _getVisibleDatasetWeightTotal: function() {
+ return this._getRingWeightOffset(this.chart.data.datasets.length);
+ }
+});
+
+core_defaults._set('horizontalBar', {
+ hover: {
+ mode: 'index',
+ axis: 'y'
+ },
+
+ scales: {
+ xAxes: [{
+ type: 'linear',
+ position: 'bottom'
+ }],
+
+ yAxes: [{
+ type: 'category',
+ position: 'left',
+ offset: true,
+ gridLines: {
+ offsetGridLines: true
+ }
+ }]
+ },
+
+ elements: {
+ rectangle: {
+ borderSkipped: 'left'
+ }
+ },
+
+ tooltips: {
+ mode: 'index',
+ axis: 'y'
+ }
+});
+
+core_defaults._set('global', {
+ datasets: {
+ horizontalBar: {
+ categoryPercentage: 0.8,
+ barPercentage: 0.9
+ }
+ }
+});
+
+var controller_horizontalBar = controller_bar.extend({
+ /**
+ * @private
+ */
+ _getValueScaleId: function() {
+ return this.getMeta().xAxisID;
+ },
+
+ /**
+ * @private
+ */
+ _getIndexScaleId: function() {
+ return this.getMeta().yAxisID;
+ }
+});
+
+var valueOrDefault$6 = helpers$1.valueOrDefault;
+var resolve$2 = helpers$1.options.resolve;
+var isPointInArea = helpers$1.canvas._isPointInArea;
+
+core_defaults._set('line', {
+ showLines: true,
+ spanGaps: false,
+
+ hover: {
+ mode: 'label'
+ },
+
+ scales: {
+ xAxes: [{
+ type: 'category',
+ id: 'x-axis-0'
+ }],
+ yAxes: [{
+ type: 'linear',
+ id: 'y-axis-0'
+ }]
+ }
+});
+
+function scaleClip(scale, halfBorderWidth) {
+ var tickOpts = scale && scale.options.ticks || {};
+ var reverse = tickOpts.reverse;
+ var min = tickOpts.min === undefined ? halfBorderWidth : 0;
+ var max = tickOpts.max === undefined ? halfBorderWidth : 0;
+ return {
+ start: reverse ? max : min,
+ end: reverse ? min : max
+ };
+}
+
+function defaultClip(xScale, yScale, borderWidth) {
+ var halfBorderWidth = borderWidth / 2;
+ var x = scaleClip(xScale, halfBorderWidth);
+ var y = scaleClip(yScale, halfBorderWidth);
+
+ return {
+ top: y.end,
+ right: x.end,
+ bottom: y.start,
+ left: x.start
+ };
+}
+
+function toClip(value) {
+ var t, r, b, l;
+
+ if (helpers$1.isObject(value)) {
+ t = value.top;
+ r = value.right;
+ b = value.bottom;
+ l = value.left;
+ } else {
+ t = r = b = l = value;
+ }
+
+ return {
+ top: t,
+ right: r,
+ bottom: b,
+ left: l
+ };
+}
+
+
+var controller_line = core_datasetController.extend({
+
+ datasetElementType: elements.Line,
+
+ dataElementType: elements.Point,
+
+ /**
+ * @private
+ */
+ _datasetElementOptions: [
+ 'backgroundColor',
+ 'borderCapStyle',
+ 'borderColor',
+ 'borderDash',
+ 'borderDashOffset',
+ 'borderJoinStyle',
+ 'borderWidth',
+ 'cubicInterpolationMode',
+ 'fill'
+ ],
+
+ /**
+ * @private
+ */
+ _dataElementOptions: {
+ backgroundColor: 'pointBackgroundColor',
+ borderColor: 'pointBorderColor',
+ borderWidth: 'pointBorderWidth',
+ hitRadius: 'pointHitRadius',
+ hoverBackgroundColor: 'pointHoverBackgroundColor',
+ hoverBorderColor: 'pointHoverBorderColor',
+ hoverBorderWidth: 'pointHoverBorderWidth',
+ hoverRadius: 'pointHoverRadius',
+ pointStyle: 'pointStyle',
+ radius: 'pointRadius',
+ rotation: 'pointRotation'
+ },
+
+ update: function(reset) {
+ var me = this;
+ var meta = me.getMeta();
+ var line = meta.dataset;
+ var points = meta.data || [];
+ var options = me.chart.options;
+ var config = me._config;
+ var showLine = me._showLine = valueOrDefault$6(config.showLine, options.showLines);
+ var i, ilen;
+
+ me._xScale = me.getScaleForId(meta.xAxisID);
+ me._yScale = me.getScaleForId(meta.yAxisID);
+
+ // Update Line
+ if (showLine) {
+ // Compatibility: If the properties are defined with only the old name, use those values
+ if (config.tension !== undefined && config.lineTension === undefined) {
+ config.lineTension = config.tension;
+ }
+
+ // Utility
+ line._scale = me._yScale;
+ line._datasetIndex = me.index;
+ // Data
+ line._children = points;
+ // Model
+ line._model = me._resolveDatasetElementOptions(line);
+
+ line.pivot();
+ }
+
+ // Update Points
+ for (i = 0, ilen = points.length; i < ilen; ++i) {
+ me.updateElement(points[i], i, reset);
+ }
+
+ if (showLine && line._model.tension !== 0) {
+ me.updateBezierControlPoints();
+ }
+
+ // Now pivot the point for animation
+ for (i = 0, ilen = points.length; i < ilen; ++i) {
+ points[i].pivot();
+ }
+ },
+
+ updateElement: function(point, index, reset) {
+ var me = this;
+ var meta = me.getMeta();
+ var custom = point.custom || {};
+ var dataset = me.getDataset();
+ var datasetIndex = me.index;
+ var value = dataset.data[index];
+ var xScale = me._xScale;
+ var yScale = me._yScale;
+ var lineModel = meta.dataset._model;
+ var x, y;
+
+ var options = me._resolveDataElementOptions(point, index);
+
+ x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex);
+ y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex);
+
+ // Utility
+ point._xScale = xScale;
+ point._yScale = yScale;
+ point._options = options;
+ point._datasetIndex = datasetIndex;
+ point._index = index;
+
+ // Desired view properties
+ point._model = {
+ x: x,
+ y: y,
+ skip: custom.skip || isNaN(x) || isNaN(y),
+ // Appearance
+ radius: options.radius,
+ pointStyle: options.pointStyle,
+ rotation: options.rotation,
+ backgroundColor: options.backgroundColor,
+ borderColor: options.borderColor,
+ borderWidth: options.borderWidth,
+ tension: valueOrDefault$6(custom.tension, lineModel ? lineModel.tension : 0),
+ steppedLine: lineModel ? lineModel.steppedLine : false,
+ // Tooltip
+ hitRadius: options.hitRadius
+ };
+ },
+
+ /**
+ * @private
+ */
+ _resolveDatasetElementOptions: function(element) {
+ var me = this;
+ var config = me._config;
+ var custom = element.custom || {};
+ var options = me.chart.options;
+ var lineOptions = options.elements.line;
+ var values = core_datasetController.prototype._resolveDatasetElementOptions.apply(me, arguments);
+
+ // The default behavior of lines is to break at null values, according
+ // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158
+ // This option gives lines the ability to span gaps
+ values.spanGaps = valueOrDefault$6(config.spanGaps, options.spanGaps);
+ values.tension = valueOrDefault$6(config.lineTension, lineOptions.tension);
+ values.steppedLine = resolve$2([custom.steppedLine, config.steppedLine, lineOptions.stepped]);
+ values.clip = toClip(valueOrDefault$6(config.clip, defaultClip(me._xScale, me._yScale, values.borderWidth)));
+
+ return values;
+ },
+
+ calculatePointY: function(value, index, datasetIndex) {
+ var me = this;
+ var chart = me.chart;
+ var yScale = me._yScale;
+ var sumPos = 0;
+ var sumNeg = 0;
+ var i, ds, dsMeta, stackedRightValue, rightValue, metasets, ilen;
+
+ if (yScale.options.stacked) {
+ rightValue = +yScale.getRightValue(value);
+ metasets = chart._getSortedVisibleDatasetMetas();
+ ilen = metasets.length;
+
+ for (i = 0; i < ilen; ++i) {
+ dsMeta = metasets[i];
+ if (dsMeta.index === datasetIndex) {
+ break;
+ }
+
+ ds = chart.data.datasets[dsMeta.index];
+ if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id) {
+ stackedRightValue = +yScale.getRightValue(ds.data[index]);
+ if (stackedRightValue < 0) {
+ sumNeg += stackedRightValue || 0;
+ } else {
+ sumPos += stackedRightValue || 0;
+ }
+ }
+ }
+
+ if (rightValue < 0) {
+ return yScale.getPixelForValue(sumNeg + rightValue);
+ }
+ return yScale.getPixelForValue(sumPos + rightValue);
+ }
+ return yScale.getPixelForValue(value);
+ },
+
+ updateBezierControlPoints: function() {
+ var me = this;
+ var chart = me.chart;
+ var meta = me.getMeta();
+ var lineModel = meta.dataset._model;
+ var area = chart.chartArea;
+ var points = meta.data || [];
+ var i, ilen, model, controlPoints;
+
+ // Only consider points that are drawn in case the spanGaps option is used
+ if (lineModel.spanGaps) {
+ points = points.filter(function(pt) {
+ return !pt._model.skip;
+ });
+ }
+
+ function capControlPoint(pt, min, max) {
+ return Math.max(Math.min(pt, max), min);
+ }
+
+ if (lineModel.cubicInterpolationMode === 'monotone') {
+ helpers$1.splineCurveMonotone(points);
+ } else {
+ for (i = 0, ilen = points.length; i < ilen; ++i) {
+ model = points[i]._model;
+ controlPoints = helpers$1.splineCurve(
+ helpers$1.previousItem(points, i)._model,
+ model,
+ helpers$1.nextItem(points, i)._model,
+ lineModel.tension
+ );
+ model.controlPointPreviousX = controlPoints.previous.x;
+ model.controlPointPreviousY = controlPoints.previous.y;
+ model.controlPointNextX = controlPoints.next.x;
+ model.controlPointNextY = controlPoints.next.y;
+ }
+ }
+
+ if (chart.options.elements.line.capBezierPoints) {
+ for (i = 0, ilen = points.length; i < ilen; ++i) {
+ model = points[i]._model;
+ if (isPointInArea(model, area)) {
+ if (i > 0 && isPointInArea(points[i - 1]._model, area)) {
+ model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);
+ model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);
+ }
+ if (i < points.length - 1 && isPointInArea(points[i + 1]._model, area)) {
+ model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);
+ model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);
+ }
+ }
+ }
+ }
+ },
+
+ draw: function() {
+ var me = this;
+ var chart = me.chart;
+ var meta = me.getMeta();
+ var points = meta.data || [];
+ var area = chart.chartArea;
+ var canvas = chart.canvas;
+ var i = 0;
+ var ilen = points.length;
+ var clip;
+
+ if (me._showLine) {
+ clip = meta.dataset._model.clip;
+
+ helpers$1.canvas.clipArea(chart.ctx, {
+ left: clip.left === false ? 0 : area.left - clip.left,
+ right: clip.right === false ? canvas.width : area.right + clip.right,
+ top: clip.top === false ? 0 : area.top - clip.top,
+ bottom: clip.bottom === false ? canvas.height : area.bottom + clip.bottom
+ });
+
+ meta.dataset.draw();
+
+ helpers$1.canvas.unclipArea(chart.ctx);
+ }
+
+ // Draw the points
+ for (; i < ilen; ++i) {
+ points[i].draw(area);
+ }
+ },
+
+ /**
+ * @protected
+ */
+ setHoverStyle: function(point) {
+ var model = point._model;
+ var options = point._options;
+ var getHoverColor = helpers$1.getHoverColor;
+
+ point.$previousStyle = {
+ backgroundColor: model.backgroundColor,
+ borderColor: model.borderColor,
+ borderWidth: model.borderWidth,
+ radius: model.radius
+ };
+
+ model.backgroundColor = valueOrDefault$6(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
+ model.borderColor = valueOrDefault$6(options.hoverBorderColor, getHoverColor(options.borderColor));
+ model.borderWidth = valueOrDefault$6(options.hoverBorderWidth, options.borderWidth);
+ model.radius = valueOrDefault$6(options.hoverRadius, options.radius);
+ },
+});
+
+var resolve$3 = helpers$1.options.resolve;
+
+core_defaults._set('polarArea', {
+ scale: {
+ type: 'radialLinear',
+ angleLines: {
+ display: false
+ },
+ gridLines: {
+ circular: true
+ },
+ pointLabels: {
+ display: false
+ },
+ ticks: {
+ beginAtZero: true
+ }
+ },
+
+ // Boolean - Whether to animate the rotation of the chart
+ animation: {
+ animateRotate: true,
+ animateScale: true
+ },
+
+ startAngle: -0.5 * Math.PI,
+ legendCallback: function(chart) {
+ var list = document.createElement('ul');
+ var data = chart.data;
+ var datasets = data.datasets;
+ var labels = data.labels;
+ var i, ilen, listItem, listItemSpan;
+
+ list.setAttribute('class', chart.id + '-legend');
+ if (datasets.length) {
+ for (i = 0, ilen = datasets[0].data.length; i < ilen; ++i) {
+ listItem = list.appendChild(document.createElement('li'));
+ listItemSpan = listItem.appendChild(document.createElement('span'));
+ listItemSpan.style.backgroundColor = datasets[0].backgroundColor[i];
+ if (labels[i]) {
+ listItem.appendChild(document.createTextNode(labels[i]));
+ }
+ }
+ }
+
+ return list.outerHTML;
+ },
+ legend: {
+ labels: {
+ generateLabels: function(chart) {
+ var data = chart.data;
+ if (data.labels.length && data.datasets.length) {
+ return data.labels.map(function(label, i) {
+ var meta = chart.getDatasetMeta(0);
+ var style = meta.controller.getStyle(i);
+
+ return {
+ text: label,
+ fillStyle: style.backgroundColor,
+ strokeStyle: style.borderColor,
+ lineWidth: style.borderWidth,
+ hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden,
+
+ // Extra data used for toggling the correct item
+ index: i
+ };
+ });
+ }
+ return [];
+ }
+ },
+
+ onClick: function(e, legendItem) {
+ var index = legendItem.index;
+ var chart = this.chart;
+ var i, ilen, meta;
+
+ for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
+ meta = chart.getDatasetMeta(i);
+ meta.data[index].hidden = !meta.data[index].hidden;
+ }
+
+ chart.update();
+ }
+ },
+
+ // Need to override these to give a nice default
+ tooltips: {
+ callbacks: {
+ title: function() {
+ return '';
+ },
+ label: function(item, data) {
+ return data.labels[item.index] + ': ' + item.yLabel;
+ }
+ }
+ }
+});
+
+var controller_polarArea = core_datasetController.extend({
+
+ dataElementType: elements.Arc,
+
+ linkScales: helpers$1.noop,
+
+ /**
+ * @private
+ */
+ _dataElementOptions: [
+ 'backgroundColor',
+ 'borderColor',
+ 'borderWidth',
+ 'borderAlign',
+ 'hoverBackgroundColor',
+ 'hoverBorderColor',
+ 'hoverBorderWidth',
+ ],
+
+ /**
+ * @private
+ */
+ _getIndexScaleId: function() {
+ return this.chart.scale.id;
+ },
+
+ /**
+ * @private
+ */
+ _getValueScaleId: function() {
+ return this.chart.scale.id;
+ },
+
+ update: function(reset) {
+ var me = this;
+ var dataset = me.getDataset();
+ var meta = me.getMeta();
+ var start = me.chart.options.startAngle || 0;
+ var starts = me._starts = [];
+ var angles = me._angles = [];
+ var arcs = meta.data;
+ var i, ilen, angle;
+
+ me._updateRadius();
+
+ meta.count = me.countVisibleElements();
+
+ for (i = 0, ilen = dataset.data.length; i < ilen; i++) {
+ starts[i] = start;
+ angle = me._computeAngle(i);
+ angles[i] = angle;
+ start += angle;
+ }
+
+ for (i = 0, ilen = arcs.length; i < ilen; ++i) {
+ arcs[i]._options = me._resolveDataElementOptions(arcs[i], i);
+ me.updateElement(arcs[i], i, reset);
+ }
+ },
+
+ /**
+ * @private
+ */
+ _updateRadius: function() {
+ var me = this;
+ var chart = me.chart;
+ var chartArea = chart.chartArea;
+ var opts = chart.options;
+ var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
+
+ chart.outerRadius = Math.max(minSize / 2, 0);
+ chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
+ chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
+
+ me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);
+ me.innerRadius = me.outerRadius - chart.radiusLength;
+ },
+
+ updateElement: function(arc, index, reset) {
+ var me = this;
+ var chart = me.chart;
+ var dataset = me.getDataset();
+ var opts = chart.options;
+ var animationOpts = opts.animation;
+ var scale = chart.scale;
+ var labels = chart.data.labels;
+
+ var centerX = scale.xCenter;
+ var centerY = scale.yCenter;
+
+ // var negHalfPI = -0.5 * Math.PI;
+ var datasetStartAngle = opts.startAngle;
+ var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
+ var startAngle = me._starts[index];
+ var endAngle = startAngle + (arc.hidden ? 0 : me._angles[index]);
+
+ var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
+ var options = arc._options || {};
+
+ helpers$1.extend(arc, {
+ // Utility
+ _datasetIndex: me.index,
+ _index: index,
+ _scale: scale,
+
+ // Desired view properties
+ _model: {
+ backgroundColor: options.backgroundColor,
+ borderColor: options.borderColor,
+ borderWidth: options.borderWidth,
+ borderAlign: options.borderAlign,
+ x: centerX,
+ y: centerY,
+ innerRadius: 0,
+ outerRadius: reset ? resetRadius : distance,
+ startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle,
+ endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle,
+ label: helpers$1.valueAtIndexOrDefault(labels, index, labels[index])
+ }
+ });
+
+ arc.pivot();
+ },
+
+ countVisibleElements: function() {
+ var dataset = this.getDataset();
+ var meta = this.getMeta();
+ var count = 0;
+
+ helpers$1.each(meta.data, function(element, index) {
+ if (!isNaN(dataset.data[index]) && !element.hidden) {
+ count++;
+ }
+ });
+
+ return count;
+ },
+
+ /**
+ * @protected
+ */
+ setHoverStyle: function(arc) {
+ var model = arc._model;
+ var options = arc._options;
+ var getHoverColor = helpers$1.getHoverColor;
+ var valueOrDefault = helpers$1.valueOrDefault;
+
+ arc.$previousStyle = {
+ backgroundColor: model.backgroundColor,
+ borderColor: model.borderColor,
+ borderWidth: model.borderWidth,
+ };
+
+ model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
+ model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor));
+ model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth);
+ },
+
+ /**
+ * @private
+ */
+ _computeAngle: function(index) {
+ var me = this;
+ var count = this.getMeta().count;
+ var dataset = me.getDataset();
+ var meta = me.getMeta();
+
+ if (isNaN(dataset.data[index]) || meta.data[index].hidden) {
+ return 0;
+ }
+
+ // Scriptable options
+ var context = {
+ chart: me.chart,
+ dataIndex: index,
+ dataset: dataset,
+ datasetIndex: me.index
+ };
+
+ return resolve$3([
+ me.chart.options.elements.arc.angle,
+ (2 * Math.PI) / count
+ ], context, index);
+ }
+});
+
+core_defaults._set('pie', helpers$1.clone(core_defaults.doughnut));
+core_defaults._set('pie', {
+ cutoutPercentage: 0
+});
+
+// Pie charts are Doughnut chart with different defaults
+var controller_pie = controller_doughnut;
+
+var valueOrDefault$7 = helpers$1.valueOrDefault;
+
+core_defaults._set('radar', {
+ spanGaps: false,
+ scale: {
+ type: 'radialLinear'
+ },
+ elements: {
+ line: {
+ fill: 'start',
+ tension: 0 // no bezier in radar
+ }
+ }
+});
+
+var controller_radar = core_datasetController.extend({
+ datasetElementType: elements.Line,
+
+ dataElementType: elements.Point,
+
+ linkScales: helpers$1.noop,
+
+ /**
+ * @private
+ */
+ _datasetElementOptions: [
+ 'backgroundColor',
+ 'borderWidth',
+ 'borderColor',
+ 'borderCapStyle',
+ 'borderDash',
+ 'borderDashOffset',
+ 'borderJoinStyle',
+ 'fill'
+ ],
+
+ /**
+ * @private
+ */
+ _dataElementOptions: {
+ backgroundColor: 'pointBackgroundColor',
+ borderColor: 'pointBorderColor',
+ borderWidth: 'pointBorderWidth',
+ hitRadius: 'pointHitRadius',
+ hoverBackgroundColor: 'pointHoverBackgroundColor',
+ hoverBorderColor: 'pointHoverBorderColor',
+ hoverBorderWidth: 'pointHoverBorderWidth',
+ hoverRadius: 'pointHoverRadius',
+ pointStyle: 'pointStyle',
+ radius: 'pointRadius',
+ rotation: 'pointRotation'
+ },
+
+ /**
+ * @private
+ */
+ _getIndexScaleId: function() {
+ return this.chart.scale.id;
+ },
+
+ /**
+ * @private
+ */
+ _getValueScaleId: function() {
+ return this.chart.scale.id;
+ },
+
+ update: function(reset) {
+ var me = this;
+ var meta = me.getMeta();
+ var line = meta.dataset;
+ var points = meta.data || [];
+ var scale = me.chart.scale;
+ var config = me._config;
+ var i, ilen;
+
+ // Compatibility: If the properties are defined with only the old name, use those values
+ if (config.tension !== undefined && config.lineTension === undefined) {
+ config.lineTension = config.tension;
+ }
+
+ // Utility
+ line._scale = scale;
+ line._datasetIndex = me.index;
+ // Data
+ line._children = points;
+ line._loop = true;
+ // Model
+ line._model = me._resolveDatasetElementOptions(line);
+
+ line.pivot();
+
+ // Update Points
+ for (i = 0, ilen = points.length; i < ilen; ++i) {
+ me.updateElement(points[i], i, reset);
+ }
+
+ // Update bezier control points
+ me.updateBezierControlPoints();
+
+ // Now pivot the point for animation
+ for (i = 0, ilen = points.length; i < ilen; ++i) {
+ points[i].pivot();
+ }
+ },
+
+ updateElement: function(point, index, reset) {
+ var me = this;
+ var custom = point.custom || {};
+ var dataset = me.getDataset();
+ var scale = me.chart.scale;
+ var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]);
+ var options = me._resolveDataElementOptions(point, index);
+ var lineModel = me.getMeta().dataset._model;
+ var x = reset ? scale.xCenter : pointPosition.x;
+ var y = reset ? scale.yCenter : pointPosition.y;
+
+ // Utility
+ point._scale = scale;
+ point._options = options;
+ point._datasetIndex = me.index;
+ point._index = index;
+
+ // Desired view properties
+ point._model = {
+ x: x, // value not used in dataset scale, but we want a consistent API between scales
+ y: y,
+ skip: custom.skip || isNaN(x) || isNaN(y),
+ // Appearance
+ radius: options.radius,
+ pointStyle: options.pointStyle,
+ rotation: options.rotation,
+ backgroundColor: options.backgroundColor,
+ borderColor: options.borderColor,
+ borderWidth: options.borderWidth,
+ tension: valueOrDefault$7(custom.tension, lineModel ? lineModel.tension : 0),
+
+ // Tooltip
+ hitRadius: options.hitRadius
+ };
+ },
+
+ /**
+ * @private
+ */
+ _resolveDatasetElementOptions: function() {
+ var me = this;
+ var config = me._config;
+ var options = me.chart.options;
+ var values = core_datasetController.prototype._resolveDatasetElementOptions.apply(me, arguments);
+
+ values.spanGaps = valueOrDefault$7(config.spanGaps, options.spanGaps);
+ values.tension = valueOrDefault$7(config.lineTension, options.elements.line.tension);
+
+ return values;
+ },
+
+ updateBezierControlPoints: function() {
+ var me = this;
+ var meta = me.getMeta();
+ var area = me.chart.chartArea;
+ var points = meta.data || [];
+ var i, ilen, model, controlPoints;
+
+ // Only consider points that are drawn in case the spanGaps option is used
+ if (meta.dataset._model.spanGaps) {
+ points = points.filter(function(pt) {
+ return !pt._model.skip;
+ });
+ }
+
+ function capControlPoint(pt, min, max) {
+ return Math.max(Math.min(pt, max), min);
+ }
+
+ for (i = 0, ilen = points.length; i < ilen; ++i) {
+ model = points[i]._model;
+ controlPoints = helpers$1.splineCurve(
+ helpers$1.previousItem(points, i, true)._model,
+ model,
+ helpers$1.nextItem(points, i, true)._model,
+ model.tension
+ );
+
+ // Prevent the bezier going outside of the bounds of the graph
+ model.controlPointPreviousX = capControlPoint(controlPoints.previous.x, area.left, area.right);
+ model.controlPointPreviousY = capControlPoint(controlPoints.previous.y, area.top, area.bottom);
+ model.controlPointNextX = capControlPoint(controlPoints.next.x, area.left, area.right);
+ model.controlPointNextY = capControlPoint(controlPoints.next.y, area.top, area.bottom);
+ }
+ },
+
+ setHoverStyle: function(point) {
+ var model = point._model;
+ var options = point._options;
+ var getHoverColor = helpers$1.getHoverColor;
+
+ point.$previousStyle = {
+ backgroundColor: model.backgroundColor,
+ borderColor: model.borderColor,
+ borderWidth: model.borderWidth,
+ radius: model.radius
+ };
+
+ model.backgroundColor = valueOrDefault$7(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
+ model.borderColor = valueOrDefault$7(options.hoverBorderColor, getHoverColor(options.borderColor));
+ model.borderWidth = valueOrDefault$7(options.hoverBorderWidth, options.borderWidth);
+ model.radius = valueOrDefault$7(options.hoverRadius, options.radius);
+ }
+});
+
+core_defaults._set('scatter', {
+ hover: {
+ mode: 'single'
+ },
+
+ scales: {
+ xAxes: [{
+ id: 'x-axis-1', // need an ID so datasets can reference the scale
+ type: 'linear', // scatter should not use a category axis
+ position: 'bottom'
+ }],
+ yAxes: [{
+ id: 'y-axis-1',
+ type: 'linear',
+ position: 'left'
+ }]
+ },
+
+ tooltips: {
+ callbacks: {
+ title: function() {
+ return ''; // doesn't make sense for scatter since data are formatted as a point
+ },
+ label: function(item) {
+ return '(' + item.xLabel + ', ' + item.yLabel + ')';
+ }
+ }
+ }
+});
+
+core_defaults._set('global', {
+ datasets: {
+ scatter: {
+ showLine: false
+ }
+ }
+});
+
+// Scatter charts use line controllers
+var controller_scatter = controller_line;
+
+// NOTE export a map in which the key represents the controller type, not
+// the class, and so must be CamelCase in order to be correctly retrieved
+// by the controller in core.controller.js (`controllers[meta.type]`).
+
+var controllers = {
+ bar: controller_bar,
+ bubble: controller_bubble,
+ doughnut: controller_doughnut,
+ horizontalBar: controller_horizontalBar,
+ line: controller_line,
+ polarArea: controller_polarArea,
+ pie: controller_pie,
+ radar: controller_radar,
+ scatter: controller_scatter
+};
+
+/**
+ * Helper function to get relative position for an event
+ * @param {Event|IEvent} event - The event to get the position for
+ * @param {Chart} chart - The chart
+ * @returns {object} the event position
+ */
+function getRelativePosition(e, chart) {
+ if (e.native) {
+ return {
+ x: e.x,
+ y: e.y
+ };
+ }
+
+ return helpers$1.getRelativePosition(e, chart);
+}
+
+/**
+ * Helper function to traverse all of the visible elements in the chart
+ * @param {Chart} chart - the chart
+ * @param {function} handler - the callback to execute for each visible item
+ */
+function parseVisibleItems(chart, handler) {
+ var metasets = chart._getSortedVisibleDatasetMetas();
+ var metadata, i, j, ilen, jlen, element;
+
+ for (i = 0, ilen = metasets.length; i < ilen; ++i) {
+ metadata = metasets[i].data;
+ for (j = 0, jlen = metadata.length; j < jlen; ++j) {
+ element = metadata[j];
+ if (!element._view.skip) {
+ handler(element);
+ }
+ }
+ }
+}
+
+/**
+ * Helper function to get the items that intersect the event position
+ * @param {ChartElement[]} items - elements to filter
+ * @param {object} position - the point to be nearest to
+ * @return {ChartElement[]} the nearest items
+ */
+function getIntersectItems(chart, position) {
+ var elements = [];
+
+ parseVisibleItems(chart, function(element) {
+ if (element.inRange(position.x, position.y)) {
+ elements.push(element);
+ }
+ });
+
+ return elements;
+}
+
+/**
+ * Helper function to get the items nearest to the event position considering all visible items in teh chart
+ * @param {Chart} chart - the chart to look at elements from
+ * @param {object} position - the point to be nearest to
+ * @param {boolean} intersect - if true, only consider items that intersect the position
+ * @param {function} distanceMetric - function to provide the distance between points
+ * @return {ChartElement[]} the nearest items
+ */
+function getNearestItems(chart, position, intersect, distanceMetric) {
+ var minDistance = Number.POSITIVE_INFINITY;
+ var nearestItems = [];
+
+ parseVisibleItems(chart, function(element) {
+ if (intersect && !element.inRange(position.x, position.y)) {
+ return;
+ }
+
+ var center = element.getCenterPoint();
+ var distance = distanceMetric(position, center);
+ if (distance < minDistance) {
+ nearestItems = [element];
+ minDistance = distance;
+ } else if (distance === minDistance) {
+ // Can have multiple items at the same distance in which case we sort by size
+ nearestItems.push(element);
+ }
+ });
+
+ return nearestItems;
+}
+
+/**
+ * Get a distance metric function for two points based on the
+ * axis mode setting
+ * @param {string} axis - the axis mode. x|y|xy
+ */
+function getDistanceMetricForAxis(axis) {
+ var useX = axis.indexOf('x') !== -1;
+ var useY = axis.indexOf('y') !== -1;
+
+ return function(pt1, pt2) {
+ var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;
+ var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;
+ return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
+ };
+}
+
+function indexMode(chart, e, options) {
+ var position = getRelativePosition(e, chart);
+ // Default axis for index mode is 'x' to match old behaviour
+ options.axis = options.axis || 'x';
+ var distanceMetric = getDistanceMetricForAxis(options.axis);
+ var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
+ var elements = [];
+
+ if (!items.length) {
+ return [];
+ }
+
+ chart._getSortedVisibleDatasetMetas().forEach(function(meta) {
+ var element = meta.data[items[0]._index];
+
+ // don't count items that are skipped (null data)
+ if (element && !element._view.skip) {
+ elements.push(element);
+ }
+ });
+
+ return elements;
+}
+
+/**
+ * @interface IInteractionOptions
+ */
+/**
+ * If true, only consider items that intersect the point
+ * @name IInterfaceOptions#boolean
+ * @type Boolean
+ */
+
+/**
+ * Contains interaction related functions
+ * @namespace Chart.Interaction
+ */
+var core_interaction = {
+ // Helper function for different modes
+ modes: {
+ single: function(chart, e) {
+ var position = getRelativePosition(e, chart);
+ var elements = [];
+
+ parseVisibleItems(chart, function(element) {
+ if (element.inRange(position.x, position.y)) {
+ elements.push(element);
+ return elements;
+ }
+ });
+
+ return elements.slice(0, 1);
+ },
+
+ /**
+ * @function Chart.Interaction.modes.label
+ * @deprecated since version 2.4.0
+ * @todo remove at version 3
+ * @private
+ */
+ label: indexMode,
+
+ /**
+ * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something
+ * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item
+ * @function Chart.Interaction.modes.index
+ * @since v2.4.0
+ * @param {Chart} chart - the chart we are returning items from
+ * @param {Event} e - the event we are find things at
+ * @param {IInteractionOptions} options - options to use during interaction
+ * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
+ */
+ index: indexMode,
+
+ /**
+ * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something
+ * If the options.intersect is false, we find the nearest item and return the items in that dataset
+ * @function Chart.Interaction.modes.dataset
+ * @param {Chart} chart - the chart we are returning items from
+ * @param {Event} e - the event we are find things at
+ * @param {IInteractionOptions} options - options to use during interaction
+ * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
+ */
+ dataset: function(chart, e, options) {
+ var position = getRelativePosition(e, chart);
+ options.axis = options.axis || 'xy';
+ var distanceMetric = getDistanceMetricForAxis(options.axis);
+ var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
+
+ if (items.length > 0) {
+ items = chart.getDatasetMeta(items[0]._datasetIndex).data;
+ }
+
+ return items;
+ },
+
+ /**
+ * @function Chart.Interaction.modes.x-axis
+ * @deprecated since version 2.4.0. Use index mode and intersect == true
+ * @todo remove at version 3
+ * @private
+ */
+ 'x-axis': function(chart, e) {
+ return indexMode(chart, e, {intersect: false});
+ },
+
+ /**
+ * Point mode returns all elements that hit test based on the event position
+ * of the event
+ * @function Chart.Interaction.modes.intersect
+ * @param {Chart} chart - the chart we are returning items from
+ * @param {Event} e - the event we are find things at
+ * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
+ */
+ point: function(chart, e) {
+ var position = getRelativePosition(e, chart);
+ return getIntersectItems(chart, position);
+ },
+
+ /**
+ * nearest mode returns the element closest to the point
+ * @function Chart.Interaction.modes.intersect
+ * @param {Chart} chart - the chart we are returning items from
+ * @param {Event} e - the event we are find things at
+ * @param {IInteractionOptions} options - options to use
+ * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
+ */
+ nearest: function(chart, e, options) {
+ var position = getRelativePosition(e, chart);
+ options.axis = options.axis || 'xy';
+ var distanceMetric = getDistanceMetricForAxis(options.axis);
+ return getNearestItems(chart, position, options.intersect, distanceMetric);
+ },
+
+ /**
+ * x mode returns the elements that hit-test at the current x coordinate
+ * @function Chart.Interaction.modes.x
+ * @param {Chart} chart - the chart we are returning items from
+ * @param {Event} e - the event we are find things at
+ * @param {IInteractionOptions} options - options to use
+ * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
+ */
+ x: function(chart, e, options) {
+ var position = getRelativePosition(e, chart);
+ var items = [];
+ var intersectsItem = false;
+
+ parseVisibleItems(chart, function(element) {
+ if (element.inXRange(position.x)) {
+ items.push(element);
+ }
+
+ if (element.inRange(position.x, position.y)) {
+ intersectsItem = true;
+ }
+ });
+
+ // If we want to trigger on an intersect and we don't have any items
+ // that intersect the position, return nothing
+ if (options.intersect && !intersectsItem) {
+ items = [];
+ }
+ return items;
+ },
+
+ /**
+ * y mode returns the elements that hit-test at the current y coordinate
+ * @function Chart.Interaction.modes.y
+ * @param {Chart} chart - the chart we are returning items from
+ * @param {Event} e - the event we are find things at
+ * @param {IInteractionOptions} options - options to use
+ * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
+ */
+ y: function(chart, e, options) {
+ var position = getRelativePosition(e, chart);
+ var items = [];
+ var intersectsItem = false;
+
+ parseVisibleItems(chart, function(element) {
+ if (element.inYRange(position.y)) {
+ items.push(element);
+ }
+
+ if (element.inRange(position.x, position.y)) {
+ intersectsItem = true;
+ }
+ });
+
+ // If we want to trigger on an intersect and we don't have any items
+ // that intersect the position, return nothing
+ if (options.intersect && !intersectsItem) {
+ items = [];
+ }
+ return items;
+ }
+ }
+};
+
+var extend = helpers$1.extend;
+
+function filterByPosition(array, position) {
+ return helpers$1.where(array, function(v) {
+ return v.pos === position;
+ });
+}
+
+function sortByWeight(array, reverse) {
+ return array.sort(function(a, b) {
+ var v0 = reverse ? b : a;
+ var v1 = reverse ? a : b;
+ return v0.weight === v1.weight ?
+ v0.index - v1.index :
+ v0.weight - v1.weight;
+ });
+}
+
+function wrapBoxes(boxes) {
+ var layoutBoxes = [];
+ var i, ilen, box;
+
+ for (i = 0, ilen = (boxes || []).length; i < ilen; ++i) {
+ box = boxes[i];
+ layoutBoxes.push({
+ index: i,
+ box: box,
+ pos: box.position,
+ horizontal: box.isHorizontal(),
+ weight: box.weight
+ });
+ }
+ return layoutBoxes;
+}
+
+function setLayoutDims(layouts, params) {
+ var i, ilen, layout;
+ for (i = 0, ilen = layouts.length; i < ilen; ++i) {
+ layout = layouts[i];
+ // store width used instead of chartArea.w in fitBoxes
+ layout.width = layout.horizontal
+ ? layout.box.fullWidth && params.availableWidth
+ : params.vBoxMaxWidth;
+ // store height used instead of chartArea.h in fitBoxes
+ layout.height = layout.horizontal && params.hBoxMaxHeight;
+ }
+}
+
+function buildLayoutBoxes(boxes) {
+ var layoutBoxes = wrapBoxes(boxes);
+ var left = sortByWeight(filterByPosition(layoutBoxes, 'left'), true);
+ var right = sortByWeight(filterByPosition(layoutBoxes, 'right'));
+ var top = sortByWeight(filterByPosition(layoutBoxes, 'top'), true);
+ var bottom = sortByWeight(filterByPosition(layoutBoxes, 'bottom'));
+
+ return {
+ leftAndTop: left.concat(top),
+ rightAndBottom: right.concat(bottom),
+ chartArea: filterByPosition(layoutBoxes, 'chartArea'),
+ vertical: left.concat(right),
+ horizontal: top.concat(bottom)
+ };
+}
+
+function getCombinedMax(maxPadding, chartArea, a, b) {
+ return Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]);
+}
+
+function updateDims(chartArea, params, layout) {
+ var box = layout.box;
+ var maxPadding = chartArea.maxPadding;
+ var newWidth, newHeight;
+
+ if (layout.size) {
+ // this layout was already counted for, lets first reduce old size
+ chartArea[layout.pos] -= layout.size;
+ }
+ layout.size = layout.horizontal ? box.height : box.width;
+ chartArea[layout.pos] += layout.size;
+
+ if (box.getPadding) {
+ var boxPadding = box.getPadding();
+ maxPadding.top = Math.max(maxPadding.top, boxPadding.top);
+ maxPadding.left = Math.max(maxPadding.left, boxPadding.left);
+ maxPadding.bottom = Math.max(maxPadding.bottom, boxPadding.bottom);
+ maxPadding.right = Math.max(maxPadding.right, boxPadding.right);
+ }
+
+ newWidth = params.outerWidth - getCombinedMax(maxPadding, chartArea, 'left', 'right');
+ newHeight = params.outerHeight - getCombinedMax(maxPadding, chartArea, 'top', 'bottom');
+
+ if (newWidth !== chartArea.w || newHeight !== chartArea.h) {
+ chartArea.w = newWidth;
+ chartArea.h = newHeight;
+
+ // return true if chart area changed in layout's direction
+ var sizes = layout.horizontal ? [newWidth, chartArea.w] : [newHeight, chartArea.h];
+ return sizes[0] !== sizes[1] && (!isNaN(sizes[0]) || !isNaN(sizes[1]));
+ }
+}
+
+function handleMaxPadding(chartArea) {
+ var maxPadding = chartArea.maxPadding;
+
+ function updatePos(pos) {
+ var change = Math.max(maxPadding[pos] - chartArea[pos], 0);
+ chartArea[pos] += change;
+ return change;
+ }
+ chartArea.y += updatePos('top');
+ chartArea.x += updatePos('left');
+ updatePos('right');
+ updatePos('bottom');
+}
+
+function getMargins(horizontal, chartArea) {
+ var maxPadding = chartArea.maxPadding;
+
+ function marginForPositions(positions) {
+ var margin = {left: 0, top: 0, right: 0, bottom: 0};
+ positions.forEach(function(pos) {
+ margin[pos] = Math.max(chartArea[pos], maxPadding[pos]);
+ });
+ return margin;
+ }
+
+ return horizontal
+ ? marginForPositions(['left', 'right'])
+ : marginForPositions(['top', 'bottom']);
+}
+
+function fitBoxes(boxes, chartArea, params) {
+ var refitBoxes = [];
+ var i, ilen, layout, box, refit, changed;
+
+ for (i = 0, ilen = boxes.length; i < ilen; ++i) {
+ layout = boxes[i];
+ box = layout.box;
+
+ box.update(
+ layout.width || chartArea.w,
+ layout.height || chartArea.h,
+ getMargins(layout.horizontal, chartArea)
+ );
+ if (updateDims(chartArea, params, layout)) {
+ changed = true;
+ if (refitBoxes.length) {
+ // Dimensions changed and there were non full width boxes before this
+ // -> we have to refit those
+ refit = true;
+ }
+ }
+ if (!box.fullWidth) { // fullWidth boxes don't need to be re-fitted in any case
+ refitBoxes.push(layout);
+ }
+ }
+
+ return refit ? fitBoxes(refitBoxes, chartArea, params) || changed : changed;
+}
+
+function placeBoxes(boxes, chartArea, params) {
+ var userPadding = params.padding;
+ var x = chartArea.x;
+ var y = chartArea.y;
+ var i, ilen, layout, box;
+
+ for (i = 0, ilen = boxes.length; i < ilen; ++i) {
+ layout = boxes[i];
+ box = layout.box;
+ if (layout.horizontal) {
+ box.left = box.fullWidth ? userPadding.left : chartArea.left;
+ box.right = box.fullWidth ? params.outerWidth - userPadding.right : chartArea.left + chartArea.w;
+ box.top = y;
+ box.bottom = y + box.height;
+ box.width = box.right - box.left;
+ y = box.bottom;
+ } else {
+ box.left = x;
+ box.right = x + box.width;
+ box.top = chartArea.top;
+ box.bottom = chartArea.top + chartArea.h;
+ box.height = box.bottom - box.top;
+ x = box.right;
+ }
+ }
+
+ chartArea.x = x;
+ chartArea.y = y;
+}
+
+core_defaults._set('global', {
+ layout: {
+ padding: {
+ top: 0,
+ right: 0,
+ bottom: 0,
+ left: 0
+ }
+ }
+});
+
+/**
+ * @interface ILayoutItem
+ * @prop {string} position - The position of the item in the chart layout. Possible values are
+ * 'left', 'top', 'right', 'bottom', and 'chartArea'
+ * @prop {number} weight - The weight used to sort the item. Higher weights are further away from the chart area
+ * @prop {boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down
+ * @prop {function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom)
+ * @prop {function} update - Takes two parameters: width and height. Returns size of item
+ * @prop {function} getPadding - Returns an object with padding on the edges
+ * @prop {number} width - Width of item. Must be valid after update()
+ * @prop {number} height - Height of item. Must be valid after update()
+ * @prop {number} left - Left edge of the item. Set by layout system and cannot be used in update
+ * @prop {number} top - Top edge of the item. Set by layout system and cannot be used in update
+ * @prop {number} right - Right edge of the item. Set by layout system and cannot be used in update
+ * @prop {number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update
+ */
+
+// The layout service is very self explanatory. It's responsible for the layout within a chart.
+// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need
+// It is this service's responsibility of carrying out that layout.
+var core_layouts = {
+ defaults: {},
+
+ /**
+ * Register a box to a chart.
+ * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title.
+ * @param {Chart} chart - the chart to use
+ * @param {ILayoutItem} item - the item to add to be layed out
+ */
+ addBox: function(chart, item) {
+ if (!chart.boxes) {
+ chart.boxes = [];
+ }
+
+ // initialize item with default values
+ item.fullWidth = item.fullWidth || false;
+ item.position = item.position || 'top';
+ item.weight = item.weight || 0;
+ item._layers = item._layers || function() {
+ return [{
+ z: 0,
+ draw: function() {
+ item.draw.apply(item, arguments);
+ }
+ }];
+ };
+ if(Array.isArray(chart.boxes)){
+ chart.boxes.push(item);
+ }
+ },
+
+ /**
+ * Remove a layoutItem from a chart
+ * @param {Chart} chart - the chart to remove the box from
+ * @param {ILayoutItem} layoutItem - the item to remove from the layout
+ */
+ removeBox: function(chart, layoutItem) {
+ if(chart.boxes){
+ if(layoutItem){
+ if(Array.isArray(chart.boxes)){
+ var index = chart.boxes.indexOf(layoutItem)
+ chart.boxes.splice(index, 1);
+ }
+ }
+ }
+ },
+
+ /**
+ * Sets (or updates) options on the given `item`.
+ * @param {Chart} chart - the chart in which the item lives (or will be added to)
+ * @param {ILayoutItem} item - the item to configure with the given options
+ * @param {object} options - the new item options.
+ */
+ configure: function(chart, item, options) {
+ var props = ['fullWidth', 'position', 'weight'];
+ var ilen = props.length;
+ var i = 0;
+ var prop;
+
+ for (; i < ilen; ++i) {
+ prop = props[i];
+ if (options.hasOwnProperty(prop)) {
+ item[prop] = options[prop];
+ }
+ }
+ },
+
+ /**
+ * Fits boxes of the given chart into the given size by having each box measure itself
+ * then running a fitting algorithm
+ * @param {Chart} chart - the chart
+ * @param {number} width - the width to fit into
+ * @param {number} height - the height to fit into
+ */
+ update: function(chart, width, height) {
+ if (!chart) {
+ return;
+ }
+
+ var layoutOptions = chart.options.layout || {};
+ var padding = helpers$1.options.toPadding(layoutOptions.padding);
+
+ var availableWidth = width - padding.width;
+ var availableHeight = height - padding.height;
+ var boxes = buildLayoutBoxes(chart.boxes);
+ var verticalBoxes = boxes.vertical;
+ var horizontalBoxes = boxes.horizontal;
+
+ // Essentially we now have any number of boxes on each of the 4 sides.
+ // Our canvas looks like the following.
+ // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
+ // B1 is the bottom axis
+ // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays
+ // These locations are single-box locations only, when trying to register a chartArea location that is already taken,
+ // an error will be thrown.
+ //
+ // |----------------------------------------------------|
+ // | T1 (Full Width) |
+ // |----------------------------------------------------|
+ // | | | T2 | |
+ // | |----|-------------------------------------|----|
+ // | | | C1 | | C2 | |
+ // | | |----| |----| |
+ // | | | | |
+ // | L1 | L2 | ChartArea (C0) | R1 |
+ // | | | | |
+ // | | |----| |----| |
+ // | | | C3 | | C4 | |
+ // | |----|-------------------------------------|----|
+ // | | | B1 | |
+ // |----------------------------------------------------|
+ // | B2 (Full Width) |
+ // |----------------------------------------------------|
+ //
+
+ var params = Object.freeze({
+ outerWidth: width,
+ outerHeight: height,
+ padding: padding,
+ availableWidth: availableWidth,
+ vBoxMaxWidth: availableWidth / 2 / verticalBoxes.length,
+ hBoxMaxHeight: availableHeight / 2
+ });
+ var chartArea = extend({
+ maxPadding: extend({}, padding),
+ w: availableWidth,
+ h: availableHeight,
+ x: padding.left,
+ y: padding.top
+ }, padding);
+
+ setLayoutDims(verticalBoxes.concat(horizontalBoxes), params);
+
+ // First fit vertical boxes
+ fitBoxes(verticalBoxes, chartArea, params);
+
+ // Then fit horizontal boxes
+ if (fitBoxes(horizontalBoxes, chartArea, params)) {
+ // if the area changed, re-fit vertical boxes
+ fitBoxes(verticalBoxes, chartArea, params);
+ }
+
+ handleMaxPadding(chartArea);
+
+ // Finally place the boxes to correct coordinates
+ placeBoxes(boxes.leftAndTop, chartArea, params);
+
+ // Move to opposite side of chart
+ chartArea.x += chartArea.w;
+ chartArea.y += chartArea.h;
+
+ placeBoxes(boxes.rightAndBottom, chartArea, params);
+
+ chart.chartArea = {
+ left: chartArea.left,
+ top: chartArea.top,
+ right: chartArea.left + chartArea.w,
+ bottom: chartArea.top + chartArea.h
+ };
+
+ // Finally update boxes in chartArea (radial scale for example)
+ helpers$1.each(boxes.chartArea, function(layout) {
+ var box = layout.box;
+ extend(box, chart.chartArea);
+ box.update(chartArea.w, chartArea.h);
+ });
+ }
+};
+
+/**
+ * Platform fallback implementation (minimal).
+ * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939
+ */
+
+var platform_basic = {
+ acquireContext: function(item) {
+ if (item && item.canvas) {
+ // Support for any object associated to a canvas (including a context2d)
+ item = item.canvas;
+ }
+
+ return item && item.getContext('2d') || null;
+ }
+};
+
+var platform_dom = "/*\n * DOM element rendering detection\n * https://davidwalsh.name/detect-node-insertion\n */\n@keyframes chartjs-render-animation {\n\tfrom { opacity: 0.99; }\n\tto { opacity: 1; }\n}\n\n.chartjs-render-monitor {\n\tanimation: chartjs-render-animation 0.001s;\n}\n\n/*\n * DOM element resizing detection\n * https://github.com/marcj/css-element-queries\n */\n.chartjs-size-monitor,\n.chartjs-size-monitor-expand,\n.chartjs-size-monitor-shrink {\n\tposition: absolute;\n\tdirection: ltr;\n\tleft: 0;\n\ttop: 0;\n\tright: 0;\n\tbottom: 0;\n\toverflow: hidden;\n\tpointer-events: none;\n\tvisibility: hidden;\n\tz-index: -1;\n}\n\n.chartjs-size-monitor-expand > div {\n\tposition: absolute;\n\twidth: 1000000px;\n\theight: 1000000px;\n\tleft: 0;\n\ttop: 0;\n}\n\n.chartjs-size-monitor-shrink > div {\n\tposition: absolute;\n\twidth: 200%;\n\theight: 200%;\n\tleft: 0;\n\ttop: 0;\n}\n";
+
+var platform_dom$1 = /*#__PURE__*/Object.freeze({
+__proto__: null,
+'default': platform_dom
+});
+
+var stylesheet = getCjsExportFromNamespace(platform_dom$1);
+
+var EXPANDO_KEY = '$chartjs';
+var CSS_PREFIX = 'chartjs-';
+var CSS_SIZE_MONITOR = CSS_PREFIX + 'size-monitor';
+var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor';
+var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation';
+var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart'];
+
+/**
+ * DOM event types -> Chart.js event types.
+ * Note: only events with different types are mapped.
+ * @see https://developer.mozilla.org/en-US/docs/Web/Events
+ */
+var EVENT_TYPES = {
+ touchstart: 'mousedown',
+ touchmove: 'mousemove',
+ touchend: 'mouseup',
+ pointerenter: 'mouseenter',
+ pointerdown: 'mousedown',
+ pointermove: 'mousemove',
+ pointerup: 'mouseup',
+ pointerleave: 'mouseout',
+ pointerout: 'mouseout'
+};
+
+/**
+ * The "used" size is the final value of a dimension property after all calculations have
+ * been performed. This method uses the computed style of `element` but returns undefined
+ * if the computed style is not expressed in pixels. That can happen in some cases where
+ * `element` has a size relative to its parent and this last one is not yet displayed,
+ * for example because of `display: none` on a parent node.
+ * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
+ * @returns {number} Size in pixels or undefined if unknown.
+ */
+function readUsedSize(element, property) {
+ var value = helpers$1.getStyle(element, property);
+ var matches = value && value.match(/^(\d+)(\.\d+)?px$/);
+ return matches ? Number(matches[1]) : undefined;
+}
+
+/**
+ * Initializes the canvas style and render size without modifying the canvas display size,
+ * since responsiveness is handled by the controller.resize() method. The config is used
+ * to determine the aspect ratio to apply in case no explicit height has been specified.
+ */
+function initCanvas(canvas, config) {
+ var style = canvas.style;
+
+ // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it
+ // returns null or '' if no explicit value has been set to the canvas attribute.
+ var renderHeight = canvas.getAttribute('height');
+ var renderWidth = canvas.getAttribute('width');
+
+ // Chart.js modifies some canvas values that we want to restore on destroy
+ canvas[EXPANDO_KEY] = {
+ initial: {
+ height: renderHeight,
+ width: renderWidth,
+ style: {
+ display: style.display,
+ height: style.height,
+ width: style.width
+ }
+ }
+ };
+
+ // Force canvas to display as block to avoid extra space caused by inline
+ // elements, which would interfere with the responsive resize process.
+ // https://github.com/chartjs/Chart.js/issues/2538
+ style.display = style.display || 'block';
+
+ if (renderWidth === null || renderWidth === '') {
+ var displayWidth = readUsedSize(canvas, 'width');
+ if (displayWidth !== undefined) {
+ canvas.width = displayWidth;
+ }
+ }
+
+ if (renderHeight === null || renderHeight === '') {
+ if (canvas.style.height === '') {
+ // If no explicit render height and style height, let's apply the aspect ratio,
+ // which one can be specified by the user but also by charts as default option
+ // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2.
+ canvas.height = canvas.width / (config.options.aspectRatio || 2);
+ } else {
+ var displayHeight = readUsedSize(canvas, 'height');
+ if (displayWidth !== undefined) {
+ canvas.height = displayHeight;
+ }
+ }
+ }
+
+ return canvas;
+}
+
+/**
+ * Detects support for options object argument in addEventListener.
+ * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
+ * @private
+ */
+var supportsEventListenerOptions = (function() {
+ var supports = false;
+ try {
+ var options = Object.defineProperty({}, 'passive', {
+ // eslint-disable-next-line getter-return
+ get: function() {
+ supports = true;
+ }
+ });
+ _window.addEventListener('e', null, options);
+ } catch (e) {
+ // continue regardless of error
+ }
+ return supports;
+}());
+
+// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events.
+// https://github.com/chartjs/Chart.js/issues/4287
+var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false;
+
+function addListener(node, type, listener) {
+ node.addEventListener(type, listener, eventListenerOptions);
+}
+
+function removeListener(node, type, listener) {
+ node.removeEventListener(type, listener, eventListenerOptions);
+}
+
+function createEvent(type, chart, x, y, nativeEvent) {
+ return {
+ type: type,
+ chart: chart,
+ native: nativeEvent || null,
+ x: x !== undefined ? x : null,
+ y: y !== undefined ? y : null,
+ };
+}
+
+function fromNativeEvent(event, chart) {
+ var type = EVENT_TYPES[event.type] || event.type;
+ var pos = helpers$1.getRelativePosition(event, chart);
+ return createEvent(type, chart, pos.x, pos.y, event);
+}
+
+function throttled(fn, thisArg) {
+ var ticking = false;
+ var args = [];
+
+ return function() {
+ args = Array.prototype.slice.call(arguments);
+ thisArg = thisArg || this;
+
+ if (!ticking) {
+ ticking = true;
+ helpers$1.requestAnimFrame.call(_window, function() {
+ ticking = false;
+ fn.apply(thisArg, args);
+ });
+ }
+ };
+}
+
+function createDiv(cls) {
+ var el = document.createElement('div');
+ el.className = cls || '';
+ return el;
+}
+
+// Implementation based on https://github.com/marcj/css-element-queries
+function createResizer(handler) {
+ var maxSize = 1000000;
+
+ // NOTE(SB) Don't use innerHTML because it could be considered unsafe.
+ // https://github.com/chartjs/Chart.js/issues/5902
+ var resizer = createDiv(CSS_SIZE_MONITOR);
+ var expand = createDiv(CSS_SIZE_MONITOR + '-expand');
+ var shrink = createDiv(CSS_SIZE_MONITOR + '-shrink');
+
+ expand.appendChild(createDiv());
+ shrink.appendChild(createDiv());
+
+ resizer.appendChild(expand);
+ resizer.appendChild(shrink);
+ resizer._reset = function() {
+ expand.scrollLeft = maxSize;
+ expand.scrollTop = maxSize;
+ shrink.scrollLeft = maxSize;
+ shrink.scrollTop = maxSize;
+ };
+
+ var onScroll = function() {
+ resizer._reset();
+ handler();
+ };
+
+ addListener(expand, 'scroll', onScroll.bind(expand, 'expand'));
+ addListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink'));
+
+ return resizer;
+}
+
+// https://davidwalsh.name/detect-node-insertion
+function watchForRender(node, handler) {
+ var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});
+ var proxy = expando.renderProxy = function(e) {
+ if (e.animationName === CSS_RENDER_ANIMATION) {
+ handler();
+ }
+ };
+
+ helpers$1.each(ANIMATION_START_EVENTS, function(type) {
+ addListener(node, type, proxy);
+ });
+
+ // #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class
+ // is removed then added back immediately (same animation frame?). Accessing the
+ // `offsetParent` property will force a reflow and re-evaluate the CSS animation.
+ // https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics
+ // https://github.com/chartjs/Chart.js/issues/4737
+ expando.reflow = !!node.offsetParent;
+
+ node.classList.add(CSS_RENDER_MONITOR);
+}
+
+function unwatchForRender(node) {
+ var expando = node[EXPANDO_KEY] || {};
+ var proxy = expando.renderProxy;
+
+ if (proxy) {
+ helpers$1.each(ANIMATION_START_EVENTS, function(type) {
+ removeListener(node, type, proxy);
+ });
+
+ delete expando.renderProxy;
+ }
+
+ node.classList.remove(CSS_RENDER_MONITOR);
+}
+
+function addResizeListener(node, listener, chart) {
+ var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});
+
+ // Let's keep track of this added resizer and thus avoid DOM query when removing it.
+ var resizer = expando.resizer = createResizer(throttled(function() {
+ if (expando.resizer) {
+ var container = chart.options.maintainAspectRatio && node.parentNode;
+ var w = container ? container.clientWidth : 0;
+ listener(createEvent('resize', chart));
+ if (container && container.clientWidth < w && chart.canvas) {
+ // If the container size shrank during chart resize, let's assume
+ // scrollbar appeared. So we resize again with the scrollbar visible -
+ // effectively making chart smaller and the scrollbar hidden again.
+ // Because we are inside `throttled`, and currently `ticking`, scroll
+ // events are ignored during this whole 2 resize process.
+ // If we assumed wrong and something else happened, we are resizing
+ // twice in a frame (potential performance issue)
+ listener(createEvent('resize', chart));
+ }
+ }
+ }));
+
+ // The resizer needs to be attached to the node parent, so we first need to be
+ // sure that `node` is attached to the DOM before injecting the resizer element.
+ watchForRender(node, function() {
+ if (expando.resizer) {
+ var container = node.parentNode;
+ if (container && container !== resizer.parentNode) {
+ container.insertBefore(resizer, container.firstChild);
+ }
+
+ // The container size might have changed, let's reset the resizer state.
+ resizer._reset();
+ }
+ });
+}
+
+function removeResizeListener(node) {
+ var expando = node[EXPANDO_KEY] || {};
+ var resizer = expando.resizer;
+
+ delete expando.resizer;
+ unwatchForRender(node);
+
+ if (resizer && resizer.parentNode) {
+ resizer.parentNode.removeChild(resizer);
+ }
+}
+
+/**
+ * Injects CSS styles inline if the styles are not already present.
+ * @param {HTMLDocument|ShadowRoot} rootNode - the node to contain the