Qt5.15.2 compatible

This commit is contained in:
zhuzichu
2023-08-24 15:50:37 +08:00
parent cb34c9e48d
commit be5e8a4a88
324 changed files with 40654 additions and 365 deletions

View File

@ -11,7 +11,8 @@ if(APPLE)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE)
endif()
find_package(Qt6 REQUIRED COMPONENTS Core Quick Qml)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Quick Qml)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Quick Qml)
if(QT_VERSION VERSION_GREATER_EQUAL "6.3")
qt_standard_project_setup()
@ -28,25 +29,34 @@ foreach(filepath ${CPP_FILES})
list(APPEND sources_files ${filename})
endforeach(filepath)
#遍历所有qml文件
file(GLOB_RECURSE QML_PATHS *.qml)
foreach(filepath ${QML_PATHS})
string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath})
list(APPEND qml_files ${filename})
endforeach(filepath)
if(QT_VERSION VERSION_GREATER_EQUAL "6.2")
#删除fluentuiplugin.cpp与fluentuiplugin.h这些只要Qt5使用Qt6不需要
list(REMOVE_ITEM sources_files fluentuiplugin.h fluentuiplugin.cpp)
#遍历所有资源文件
file(GLOB_RECURSE RES_PATHS *.png *.jpg *.svg *.ico *.ttf *.webp *.js)
foreach(filepath ${RES_PATHS})
string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath})
list(APPEND resource_files ${filename})
endforeach(filepath)
#遍历所有qml文件
file(GLOB_RECURSE QML_PATHS *.qml)
foreach(filepath ${QML_PATHS})
if(${filepath} MATCHES "Qt${QT_VERSION_MAJOR}/")
string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath})
list(APPEND qml_files ${filename})
endif()
endforeach(filepath)
#修改资源文件导出路径
foreach(filepath IN LISTS qml_files resource_files)
string(REPLACE "imports/FluentUI/" "" filename ${filepath})
set_source_files_properties(${filepath} PROPERTIES QT_RESOURCE_ALIAS ${filename})
endforeach()
#遍历所有资源文件
file(GLOB_RECURSE RES_PATHS *.png *.jpg *.svg *.ico *.ttf *.webp *.js)
foreach(filepath ${RES_PATHS})
if(${filepath} MATCHES "Qt${QT_VERSION_MAJOR}/")
string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath})
list(APPEND resource_files ${filename})
endif()
endforeach(filepath)
#修改资源文件导出路径
foreach(filepath IN LISTS qml_files resource_files)
string(REPLACE "Qt${QT_VERSION_MAJOR}/imports/FluentUI/" "" filename ${filepath})
set_source_files_properties(${filepath} PROPERTIES QT_RESOURCE_ALIAS ${filename})
endforeach()
endif()
#添加qml模块
if (FLUENTUI_BUILD_STATIC_LIB)
@ -54,7 +64,6 @@ if (FLUENTUI_BUILD_STATIC_LIB)
else()
set(LIB_TYPE "SHARED")
endif()
qt_add_library(${PROJECT_NAME} ${LIB_TYPE})
if (FLUENTUI_BUILD_STATIC_LIB)
set(PLUGIN_TARGET_NAME "")
@ -73,24 +82,38 @@ if(WIN32)
)
endif()
qt_add_qml_module(${PROJECT_NAME}
PLUGIN_TARGET ${PLUGIN_TARGET_NAME}
OUTPUT_DIRECTORY ${FLUENTUI_QML_PLUGIN_DIRECTORY}
VERSION 1.0
URI "FluentUI"
#修改qmltypes文件名称。默认fluentuiplugin.qmltypes使用默认名称有时候import FluentUI会爆红所以修改成plugins.qmltypes
TYPEINFO "plugins.qmltypes"
SOURCES ${sources_files} ${FLUENTUI_VERSION_RC_PATH}
QML_FILES ${qml_files}
RESOURCES ${resource_files}
RESOURCE_PREFIX "/"
)
if(QT_VERSION VERSION_GREATER_EQUAL "6.2")
qt_add_library(${PROJECT_NAME} ${LIB_TYPE})
qt_add_qml_module(${PROJECT_NAME}
PLUGIN_TARGET ${PLUGIN_TARGET_NAME}
OUTPUT_DIRECTORY ${FLUENTUI_QML_PLUGIN_DIRECTORY}
VERSION 1.0
URI "FluentUI"
#修改qmltypes文件名称。默认fluentuiplugin.qmltypes使用默认名称有时候import FluentUI 1.0会爆红所以修改成plugins.qmltypes
TYPEINFO "plugins.qmltypes"
SOURCES ${sources_files} ${FLUENTUI_VERSION_RC_PATH}
QML_FILES ${qml_files}
RESOURCES ${resource_files}
RESOURCE_PREFIX "/"
)
else()
include(QmlPlugin)
add_qmlplugin(${PROJECT_NAME}
URI "FluentUI"
VERSION 1.0
SOURCES ${sources_files} ${FLUENTUI_VERSION_RC_PATH} resource.qrc
QMLFILES ${qml_files}
QMLDIR imports/FluentUI
BINARY_DIR ${FLUENTUI_QML_PLUGIN_DIRECTORY}
LIBTYPE ${LIB_TYPE}
)
endif()
#链接库
target_link_libraries(${PROJECT_NAME} PUBLIC
Qt::CorePrivate
Qt::QuickPrivate
Qt::QmlPrivate
Qt${QT_VERSION_MAJOR}::CorePrivate
Qt${QT_VERSION_MAJOR}::QuickPrivate
Qt${QT_VERSION_MAJOR}::QmlPrivate
ZXing
)

View File

@ -15,7 +15,6 @@ Q_ENUM_NS(CaptrueMode)
QML_NAMED_ELEMENT(FluScreenshotType)
}
namespace FluThemeType {
Q_NAMESPACE
enum DarkMode {

View File

@ -1,5 +1,16 @@
#include "FluTextStyle.h"
FluTextStyle* FluTextStyle::m_instance = nullptr;
FluTextStyle *FluTextStyle::getInstance()
{
if(FluTextStyle::m_instance == nullptr){
FluTextStyle::m_instance = new FluTextStyle;
}
return FluTextStyle::m_instance;
}
FluTextStyle::FluTextStyle(QObject *parent)
: QObject{parent}
{

View File

@ -10,7 +10,6 @@ class FluTextStyle : public QObject
{
Q_OBJECT
public:
explicit FluTextStyle(QObject *parent = nullptr);
Q_PROPERTY_AUTO(QFont,Caption);
Q_PROPERTY_AUTO(QFont,Body);
Q_PROPERTY_AUTO(QFont,BodyStrong);
@ -20,8 +19,15 @@ public:
Q_PROPERTY_AUTO(QFont,Display);
QML_NAMED_ELEMENT(FluTextStyle)
QML_SINGLETON
signals:
private:
explicit FluTextStyle(QObject *parent = nullptr);
static FluTextStyle* m_instance;
public:
static FluTextStyle *getInstance();
static FluTextStyle *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine)
{
return getInstance();
}
};
#endif // FLUTEXTSTYLE_H

View File

@ -4,6 +4,7 @@
#include <QUuid>
#include <QCursor>
#include <QScreen>
#include <QColor>
#include <QFileInfo>
#include <QTextDocument>
@ -123,3 +124,7 @@ QString FluTools::getApplicationDirPath(){
QUrl FluTools::getUrlByFilePath(const QString& path){
return QUrl::fromLocalFile(path);
}
QColor FluTools::colorAlpha(const QColor& color,qreal alpha){
return QColor(color.red(),color.green(),color.blue(),255*alpha);
}

View File

@ -3,6 +3,7 @@
#include <QObject>
#include <QFile>
#include <QColor>
#include <QtQml/qqml.h>
/**
@ -135,6 +136,13 @@ public:
*/
Q_INVOKABLE QUrl getUrlByFilePath(const QString& path);
/**
* @brief colorAlpha
* @param color
* @param alpha
* @return
*/
Q_INVOKABLE QColor colorAlpha(const QColor&,qreal alpha);
};

View File

@ -0,0 +1,233 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import "Content"
Rectangle {
id: colorPicker
property color colorValue: "transparent"
property bool enableAlphaChannel: true
property bool enableDetails: true
property int colorHandleRadius : 8
property color _changingColorValue : _hsla(hueSlider.value, sbPicker.saturation,sbPicker.brightness, alphaSlider.value)
on_ChangingColorValueChanged: {
colorValue = _changingColorValue
}
signal colorChanged(color changedColor)
implicitWidth: picker.implicitWidth
implicitHeight: picker.implicitHeight
color: "#00000000"
clip: true
RowLayout {
id: picker
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: colorHandleRadius
anchors.bottom: parent.bottom
spacing: 0
SBPicker {
id: sbPicker
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumWidth: 200
Layout.minimumHeight: 200
hueColor: {
var v = 1.0-hueSlider.value
if(0.0 <= v && v < 0.16) {
return Qt.rgba(1.0, 0.0, v/0.16, 1.0)
} else if(0.16 <= v && v < 0.33) {
return Qt.rgba(1.0 - (v-0.16)/0.17, 0.0, 1.0, 1.0)
} else if(0.33 <= v && v < 0.5) {
return Qt.rgba(0.0, ((v-0.33)/0.17), 1.0, 1.0)
} else if(0.5 <= v && v < 0.76) {
return Qt.rgba(0.0, 1.0, 1.0 - (v-0.5)/0.26, 1.0)
} else if(0.76 <= v && v < 0.85) {
return Qt.rgba((v-0.76)/0.09, 1.0, 0.0, 1.0)
} else if(0.85 <= v && v <= 1.0) {
return Qt.rgba(1.0, 1.0 - (v-0.85)/0.15, 0.0, 1.0)
} else {
return "red"
}
}
}
Item {
id: huePicker
width: 12
Layout.fillHeight: true
Layout.topMargin: colorHandleRadius
Layout.bottomMargin: colorHandleRadius
Rectangle {
anchors.fill: parent
id: colorBar
gradient: Gradient {
GradientStop { position: 1.0; color: "#FF0000" }
GradientStop { position: 0.85; color: "#FFFF00" }
GradientStop { position: 0.76; color: "#00FF00" }
GradientStop { position: 0.5; color: "#00FFFF" }
GradientStop { position: 0.33; color: "#0000FF" }
GradientStop { position: 0.16; color: "#FF00FF" }
GradientStop { position: 0.0; color: "#FF0000" }
}
}
ColorSlider {
id: hueSlider; anchors.fill: parent
}
}
Item {
id: alphaPicker
visible: enableAlphaChannel
width: 12
Layout.leftMargin: 4
Layout.fillHeight: true
Layout.topMargin: colorHandleRadius
Layout.bottomMargin: colorHandleRadius
Checkerboard { cellSide: 4 }
Rectangle {
anchors.fill: parent
gradient: Gradient {
GradientStop { position: 0.0; color: "#FF000000" }
GradientStop { position: 1.0; color: "#00000000" }
}
}
ColorSlider {
id: alphaSlider; anchors.fill: parent
}
}
Column {
id: detailColumn
Layout.leftMargin: 4
Layout.fillHeight: true
Layout.topMargin: colorHandleRadius
Layout.bottomMargin: colorHandleRadius
Layout.alignment: Qt.AlignRight
visible: enableDetails
PanelBorder {
width: parent.width
height: 30
visible: enableAlphaChannel
Checkerboard { cellSide: 5 }
Rectangle {
width: parent.width; height: 30
border.width: 1; border.color: "black"
color: colorPicker.colorValue
}
}
Item {
width: parent.width
height: 1
}
PanelBorder {
id: colorEditBox
height: 15; width: parent.width
TextInput {
anchors.fill: parent
color: "#AAAAAA"
selectionColor: "#FF7777AA"
font.pixelSize: 11
maximumLength: 9
focus: false
text: _fullColorString(colorPicker.colorValue, alphaSlider.value)
selectByMouse: true
}
}
Item {
width: parent.width
height: 8
}
Column {
width: parent.width
spacing: 1
NumberBox { caption: "H:"; value: hueSlider.value.toFixed(2) }
NumberBox { caption: "S:"; value: sbPicker.saturation.toFixed(2) }
NumberBox { caption: "B:"; value: sbPicker.brightness.toFixed(2) }
}
Item {
width: parent.width
height: 8
}
Column {
width: parent.width
spacing: 1
NumberBox {
caption: "R:"
value: _getChannelStr(colorPicker.colorValue, 0)
min: 0; max: 255
}
NumberBox {
caption: "G:"
value: _getChannelStr(colorPicker.colorValue, 1)
min: 0; max: 255
}
NumberBox {
caption: "B:"
value: _getChannelStr(colorPicker.colorValue, 2)
min: 0; max: 255
}
}
Item{
width: parent.width
height: 1
}
NumberBox {
visible: enableAlphaChannel
caption: "A:"; value: Math.ceil(alphaSlider.value*255)
min: 0; max: 255
}
}
}
function _hsla(h, s, b, a) {
var lightness = (2 - s)*b
var satHSL = s*b/((lightness <= 1) ? lightness : 2 - lightness)
lightness /= 2
var c = Qt.hsla(h, satHSL, lightness, a)
colorChanged(c)
return c
}
function _rgb(rgb, a) {
var c = Qt.rgba(rgb.r, rgb.g, rgb.b, a)
colorChanged(c)
return c
}
function _fullColorString(clr, a) {
return "#" + ((Math.ceil(a*255) + 256).toString(16).substr(1, 2) + clr.toString().substr(1, 6)).toUpperCase()
}
function _getChannelStr(clr, channelIdx) {
return parseInt(clr.toString().substr(channelIdx*2 + 1, 2), 16)
}
function setColor(color) {
var c = Qt.tint(color, "transparent")
alphaSlider.setValue(c.a)
colorPicker.colorValue = c
}
}

View File

@ -0,0 +1,16 @@
import QtQuick 2.15
Grid {
id: root
property int cellSide: 5
anchors.fill: parent
rows: height/cellSide; columns: width/cellSide
clip: true
Repeater {
model: root.columns*root.rows
Rectangle {
width: root.cellSide; height: root.cellSide
color: (index%2 == 0) ? "gray" : "white"
}
}
}

View File

@ -0,0 +1,44 @@
import QtQuick 2.15
Item {
property int cursorHeight: 7
property real value: (1 - pickerCursor.y/height)
width: 15
height: 300
Item {
id: pickerCursor
width: parent.width
Rectangle {
x: -3; y: -height*0.5
width: parent.width + 4; height: cursorHeight
border.color: "black"; border.width: 1
color: "transparent"
Rectangle {
anchors.fill: parent; anchors.margins: 2
border.color: "white"; border.width: 1
color: "transparent"
}
}
}
MouseArea {
y: -Math.round(cursorHeight/2)
height: parent.height+cursorHeight
anchors.left: parent.left
preventStealing: true
anchors.right: parent.right
function handleMouse(mouse) {
if (mouse.buttons & Qt.LeftButton) {
pickerCursor.y = Math.max(0, Math.min(height, mouse.y)-cursorHeight)
}
}
onPositionChanged:(mouse)=> {
handleMouse(mouse)
}
onPressed:(mouse)=> handleMouse(mouse)
}
function setValue(val) {
pickerCursor.y = height * (1 - val)
}
}

View File

@ -0,0 +1,39 @@
import QtQuick 2.15
Row {
property alias caption: captionBox.text
property alias value: inputBox.text
property alias min: numValidator.bottom
property alias max: numValidator.top
property alias decimals: numValidator.decimals
width: 80;
height: 15
spacing: 4
//anchors.margins: 2
Text {
id: captionBox
width: 18; height: parent.height
color: "#AAAAAA"
font.pixelSize: 11; font.bold: true
}
PanelBorder {
height: parent.height
TextInput {
id: inputBox
color: "#AAAAAA"; selectionColor: "#FF7777AA"
font.pixelSize: 11
maximumLength: 10
focus: false
readOnly: true
selectByMouse: true
validator: DoubleValidator {
id: numValidator
bottom: 0; top: 1; decimals: 2
notation: DoubleValidator.StandardNotation
}
}
}
}

View File

@ -0,0 +1,16 @@
import QtQuick 2.15
Rectangle {
width : 40; height : 15; radius: 2
border.width: 1; border.color: "#FF101010"
color: "transparent"
anchors.leftMargin: 1; anchors.topMargin: 3
clip: true
Rectangle {
anchors.fill: parent; radius: 2
anchors.leftMargin: -1; anchors.topMargin: -1
anchors.rightMargin: 0; anchors.bottomMargin: 0
border.width: 1; border.color: "#FF525255"
color: "transparent"
}
}

View File

@ -0,0 +1,62 @@
import QtQuick 2.15
Item {
id: root
property color hueColor : "blue"
property real saturation : pickerCursor.x/(width-2*r)
property real brightness : 1 - pickerCursor.y/(height-2*r)
property int r : colorHandleRadius
Rectangle {
x : r
y : r + parent.height - 2 * r
rotation: -90
transformOrigin: Item.TopLeft
width: parent.height - 2 * r
height: parent.width - 2 * r
gradient: Gradient {
GradientStop { position: 0.0; color: "#FFFFFF" }
GradientStop { position: 1.0; color: root.hueColor }
}
}
Rectangle {
x: r
y: r
width: parent.width - 2 * r
height: parent.height - 2 * r
gradient: Gradient {
GradientStop { position: 1.0; color: "#FF000000" }
GradientStop { position: 0.0; color: "#00000000" }
}
}
Item {
id: pickerCursor
Rectangle {
width: r*2; height: r*2
radius: r
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: r
y: r
preventStealing: true
function handleMouse(mouse) {
if (mouse.buttons & Qt.LeftButton) {
pickerCursor.x = Math.max(0,Math.min(mouse.x - r,width-2*r));
pickerCursor.y = Math.max(0,Math.min(mouse.y - r,height-2*r));
}
}
onPositionChanged:(mouse)=> handleMouse(mouse)
onPressed:(mouse)=> handleMouse(mouse)
}
}

View File

@ -0,0 +1,40 @@
import QtQuick 2.15
import QtGraphicalEffects 1.15
import FluentUI 1.0
FluItem {
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.066
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
}
}

View File

@ -0,0 +1,166 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
import QtQuick.Layouts 1.15
import FluentUI 1.0
Rectangle{
property string title: ""
property string darkText : "夜间模式"
property string minimizeText : "最小化"
property string restoreText : "向下还原"
property string maximizeText : "最大化"
property string closeText : "关闭"
property color textColor: FluTheme.dark ? "#FFFFFF" : "#000000"
property color minimizeNormalColor: Qt.rgba(0,0,0,0)
property color minimizeHoverColor: FluTheme.dark ? Qt.rgba(1,1,1,0.1) : Qt.rgba(0,0,0,0.06)
property color maximizeNormalColor: Qt.rgba(0,0,0,0)
property color maximizeHoverColor: FluTheme.dark ? Qt.rgba(1,1,1,0.1) : Qt.rgba(0,0,0,0.06)
property color closeNormalColor: Qt.rgba(0,0,0,0)
property color closeHoverColor: Qt.rgba(251/255,115/255,115/255,1)
property bool showDark: false
property bool showClose: true
property bool showMinimize: true
property bool showMaximize: true
property bool titleVisible: true
property url icon
property int iconSize: 20
property bool isMac: FluTools.isMacos()
property color borerlessColor : FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
property var maxClickListener : function(){
if (d.win.visibility === Window.Maximized)
d.win.visibility = Window.Windowed
else
d.win.visibility = Window.Maximized
}
property var minClickListener: function(){
d.win.visibility = Window.Minimized
}
property var closeClickListener : function(){
d.win.close()
}
property var darkClickListener: function(){
if(FluTheme.dark){
FluTheme.darkMode = FluThemeType.Light
}else{
FluTheme.darkMode = FluThemeType.Dark
}
}
id:control
color: Qt.rgba(0,0,0,0)
height: visible ? 30 : 0
opacity: visible
z: 65535
Item{
id:d
property var win: Window.window
property bool isRestore: win && Window.Maximized === win.visibility
property bool resizable: win && !(win.minimumHeight === win.maximumHeight && win.maximumWidth === win.minimumWidth)
}
TapHandler {
onTapped: if (tapCount === 2) btn_maximize.clicked()
gesturePolicy: TapHandler.DragThreshold
}
DragHandler {
target: null
grabPermissions: TapHandler.CanTakeOverFromAnything
onActiveChanged: if (active) { d.win.startSystemMove(); }
}
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
}
}
RowLayout{
anchors.right: parent.right
height: control.height
spacing: 0
FluToggleSwitch{
id:btn_dark
Layout.alignment: Qt.AlignVCenter
Layout.rightMargin: 5
visible: showDark
text:darkText
textColor:control.textColor
checked: FluTheme.dark
textRight: false
clickListener:()=> darkClickListener(btn_dark)
}
FluIconButton{
id:btn_minimize
Layout.preferredWidth: 40
Layout.preferredHeight: 30
iconSource : FluentIcons.ChromeMinimize
Layout.alignment: Qt.AlignVCenter
iconSize: 11
text:minimizeText
radius: 0
visible: !isMac && showMinimize
iconColor: control.textColor
color: hovered ? minimizeHoverColor : minimizeNormalColor
onClicked: minClickListener()
}
FluIconButton{
id:btn_maximize
Layout.preferredWidth: 40
Layout.preferredHeight: 30
iconSource : d.isRestore ? FluentIcons.ChromeRestore : FluentIcons.ChromeMaximize
color: 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
iconSource : FluentIcons.ChromeClose
Layout.alignment: Qt.AlignVCenter
text:closeText
Layout.preferredWidth: 40
Layout.preferredHeight: 30
visible: !isMac && showClose
radius: 0
iconSize: 10
iconColor: hovered ? Qt.rgba(1,1,1,1) : control.textColor
color:hovered ? closeHoverColor : closeNormalColor
onClicked: closeClickListener()
}
}
function minimizeButton(){
return btn_minimize
}
function maximizeButton(){
return btn_maximize
}
function closeButton(){
return btn_close
}
function darkButton(){
return btn_dark
}
}

View File

@ -0,0 +1,27 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
import FluentUI 1.0
Rectangle {
default property alias content: container.data
property int paddings : 0
property int leftPadding : 0
property int rightPadding : 0
property int topPadding : 0
property int bottomPadding : 0
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: container
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)
}
}

View File

@ -0,0 +1,135 @@
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
FluTextBox{
property var items:[]
property string emptyText: "没有找到结果"
property int autoSuggestBoxReplacement: FluentIcons.Search
signal itemClicked(var data)
signal handleClicked
id:control
Component.onCompleted: {
loadData()
}
Item{
id:d
property bool flagVisible: true
property var window : Window.window
}
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:{
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: {
loadData()
if(d.flagVisible){
var pos = control.mapToItem(null, 0, 0)
if(d.window.height>pos.y+control.height+container.height){
control_popup.y = control.height
} else if(pos.y>container.height){
control_popup.y = -container.height
} else {
popup.y = d.window.height-(pos.y+container.height)
}
control_popup.visible = true
}
}
function handleClick(modelData){
control_popup.visible = false
control.itemClicked(modelData)
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(item.title.indexOf(control.text)!==-1){
result.push(item)
}
})
list_view.model = result
}
}

View File

@ -0,0 +1,79 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
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
}
Text{
anchors.centerIn: parent
color: Qt.rgba(1,1,1,1)
visible: !isDot
text:{
if(count<100)
return count
return count+"+"
}
}
}

View File

@ -0,0 +1,92 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import FluentUI 1.0
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 : 0
}
}
add: Transition {
NumberAnimation {
properties: "opacity"
from: 0
to: 1
duration: FluTheme.enableAnimation ? 83 : 0
}
}
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
}
}

View File

@ -0,0 +1,63 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
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(251/255,251/255,251/255,1)
property color disableColor: FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(252/255,252/255,252/255,1)
property color 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
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
}
}

View File

@ -0,0 +1,114 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.Window 2.15
import FluentUI 1.0
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: "请选择日期"
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()
}
}
Item{
id:d
property var window : Window.window
}
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: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
dim:false
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
FluCalendarView{
id:container
onDateClicked:
(date)=>{
popup.close()
var year = date.getFullYear()
var month = date.getMonth()
var day = date.getDate()
text_date.text = year+"-"+(month+1)+"-"+day
}
}
}
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()
}
}
}

View File

@ -0,0 +1,435 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
Item {
property int displayMode: FluCalendarViewType.Month
property var date: new Date()
property var currentDate : new Date()
property var toDay: new Date()
signal dateClicked(var date)
id:control
width: 280
height: 325
Component.onCompleted: {
updateMouth(date)
}
Component{
id:com_week
Item{
height: 40
width: 40
FluText{
text:name
anchors.centerIn: parent
}
}
}
Component{
id:com_year
Button{
id:item_control
property bool isYear: control.toDay.getFullYear() === date.getFullYear()
height: 70
width: 70
onClicked:{
control.date = date
displayMode = FluCalendarViewType.Year
updateYear(date)
}
background: Item{
Rectangle{
width: 60
height: 60
radius: 4
anchors.centerIn: parent
color:{
if(FluTheme.dark){
if(item_control.hovered){
return Qt.rgba(1,1,1,0.05)
}
return Qt.rgba(0,0,0,0)
}else{
if(item_control.hovered){
return Qt.rgba(0,0,0,0.05)
}
return Qt.rgba(0,0,0,0)
}
}
}
Rectangle{
id:backgound_selected
anchors.centerIn: parent
width: 50
height: 50
radius: 25
visible: isYear
color: FluTheme.primaryColor.dark
}
FluText{
text:date.getFullYear()
anchors.centerIn: parent
color: {
if(isYear){
return "#FFFFFF"
}
if(isDecade){
return FluTheme.dark ? "#FFFFFF" : "#1A1A1A"
}
return Qt.rgba(150/255,150/255,150/255,1)
}
}
}
contentItem: Item{}
}
}
Component{
id:com_month
Button{
id:item_control
property bool isYear: control.date.getFullYear() === date.getFullYear()
property bool isMonth: control.toDay.getFullYear() === date.getFullYear() && control.toDay.getMonth() === date.getMonth()
height: 70
width: 70
onClicked:{
control.date = date
displayMode = FluCalendarViewType.Month
updateMouth(date)
}
background: Item{
Rectangle{
width: 60
height: 60
radius: 4
anchors.centerIn: parent
color:{
if(FluTheme.dark){
if(item_control.hovered){
return Qt.rgba(1,1,1,0.05)
}
return Qt.rgba(0,0,0,0)
}else{
if(item_control.hovered){
return Qt.rgba(0,0,0,0.05)
}
return Qt.rgba(0,0,0,0)
}
}
}
Rectangle{
id:backgound_selected
anchors.centerIn: parent
width: 50
height: 50
radius: 25
visible: isMonth
color: FluTheme.primaryColor.dark
}
FluText{
text:(date.getMonth()+1)+"月"
anchors.centerIn: parent
color: {
if(isMonth){
return "#FFFFFF"
}
if(isYear){
return FluTheme.dark ? "#FFFFFF" : "#1A1A1A"
}
return Qt.rgba(150/255,150/255,150/255,1)
}
}
}
contentItem: Item{}
}
}
Component{
id:com_day
Button{
id:item_control
property bool isMonth: control.date.getMonth() === date.getMonth()
property bool isDay: control.currentDate.getFullYear() === date.getFullYear() && control.currentDate.getMonth() === date.getMonth() && control.currentDate.getDate() === date.getDate()
property bool isToDay: control.toDay.getFullYear() === date.getFullYear() && control.toDay.getMonth() === date.getMonth() && control.toDay.getDate() === date.getDate()
height: 40
width: 40
onClicked: {
currentDate = date
control.dateClicked(date)
}
background: Item{
Rectangle{
width: 36
height: 36
radius: 4
anchors.centerIn: parent
color:{
if(FluTheme.dark){
if(item_control.hovered){
return Qt.rgba(1,1,1,0.05)
}
return Qt.rgba(0,0,0,0)
}else{
if(item_control.hovered){
return Qt.rgba(0,0,0,0.05)
}
return Qt.rgba(0,0,0,0)
}
}
}
Rectangle{
id:backgound_today
anchors.centerIn: parent
width: 36
height: 36
radius: 18
color:"#00000000"
visible: isDay
border.color: FluTheme.primaryColor.dark
border.width: 1
}
Rectangle{
id:backgound_selected
anchors.centerIn: parent
width: 30
height: 30
radius: 15
visible: isToDay
color: FluTheme.primaryColor.dark
}
FluText{
text:date.getDate()
anchors.centerIn: parent
color: {
if(isToDay){
return "#FFFFFF"
}
if(isMonth){
return FluTheme.dark ? "#FFFFFF" : "#1A1A1A"
}
return Qt.rgba(150/255,150/255,150/255,1)
}
}
}
contentItem: Item{}
}
}
FluArea{
anchors.fill: parent
radius: 5
FluShadow{
radius: 5
}
Rectangle{
id:layout_divider
height: 1
width: parent.width
color: FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,229/255,234/255,1)
anchors{
top: parent.top
topMargin: 44
}
}
Item{
id:layout_top
anchors{
left: parent.left
right: parent.right
top: parent.top
bottom: layout_divider.top
}
FluTextButton{
id:title
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: 14
}
disabled: displayMode === FluCalendarViewType.Decade
onClicked:{
if(displayMode === FluCalendarViewType.Month){
displayMode = FluCalendarViewType.Year
updateYear(date)
}else if(displayMode === FluCalendarViewType.Year){
displayMode = FluCalendarViewType.Decade
updateDecade(date)
}
}
}
FluIconButton{
id:icon_up
iconSource: FluentIcons.CaretUpSolid8
iconSize: 10
anchors{
verticalCenter: parent.verticalCenter
right: icon_down.left
rightMargin: 14
}
onClicked: {
var year = date.getFullYear()
var month = date.getMonth()
if(displayMode === FluCalendarViewType.Month){
var lastMonthYear = year;
var lastMonthMonth = month - 1
if (month === 0) {
lastMonthYear = year - 1
lastMonthMonth = 11
}
date = new Date(lastMonthYear,lastMonthMonth,1)
updateMouth(date)
}else if(displayMode === FluCalendarViewType.Year){
date = new Date(year-1,month,1)
updateYear(date)
}else if(displayMode === FluCalendarViewType.Decade){
date = new Date(Math.floor(year / 10) * 10-10,month,1)
updateDecade(date)
}
}
}
FluIconButton{
id:icon_down
iconSource: FluentIcons.CaretDownSolid8
iconSize: 10
anchors{
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: 8
}
onClicked: {
var year = date.getFullYear()
var month = date.getMonth()
if(displayMode === FluCalendarViewType.Month){
var nextMonthYear = year
var nextMonth = month + 1
if (month === 11) {
nextMonthYear = year + 1
nextMonth = 0
}
date = new Date(nextMonthYear,nextMonth,1)
updateMouth(date)
}else if(displayMode === FluCalendarViewType.Year){
date = new Date(year+1,month,1)
updateYear(date)
}else if(displayMode === FluCalendarViewType.Decade){
date = new Date(Math.floor(year / 10) * 10+10,month,1)
updateDecade(date)
}
}
}
}
ListModel {
id:list_model
}
Item{
id:layout_bottom
anchors{
left: parent.left
right: parent.right
top: layout_divider.bottom
bottom: parent.bottom
}
GridView{
model: list_model
anchors.fill: parent
cellHeight: displayMode === FluCalendarViewType.Month ? 40 : 70
cellWidth: displayMode === FluCalendarViewType.Month ? 40 : 70
clip: true
boundsBehavior:Flickable.StopAtBounds
delegate: Loader{
property var modelData : model
property var name : model.name
property var date : model.date
property var isDecade: {
return model.isDecade
}
sourceComponent: {
if(model.type === 0){
return com_week
}
if(model.type === 1){
return com_day
}
if(model.type === 2){
return com_month
}
if(model.type === 3){
return com_year
}
return com_day
}
}
}
}
}
function createItemWeek(name){
return {type:0,date:new Date(),name:name,isDecade:false}
}
function createItemDay(date){
return {type:1,date:date,name:"",isDecade:false}
}
function createItemMonth(date){
return {type:2,date:date,name:"",isDecade:false}
}
function createItemYear(date,isDecade){
return {type:3,date:date,name:"",isDecade:isDecade}
}
function updateDecade(date){
list_model.clear()
var year = date.getFullYear()
const decadeStart = Math.floor(year / 10) * 10;
for(var i = decadeStart ; i< decadeStart+10 ; i++){
list_model.append(createItemYear(new Date(i,0,1),true));
}
for(var j = decadeStart+10 ; j< decadeStart+16 ; j++){
list_model.append(createItemYear(new Date(j,0,1),false));
}
title.text = decadeStart+"-"+(decadeStart+10)
}
function updateYear(date){
list_model.clear()
var year = date.getFullYear()
for(var i = 0 ; i< 12 ; i++){
list_model.append(createItemMonth(new Date(year,i)));
}
for(var j = 0 ; j< 4 ; j++){
list_model.append(createItemMonth(new Date(year+1,j)));
}
title.text = year+"年"
}
function updateMouth(date){
list_model.clear()
list_model.append([createItemWeek("一"),createItemWeek("二"),createItemWeek("三"),createItemWeek("四"),createItemWeek("五"),createItemWeek("六"),createItemWeek("日")])
var year = date.getFullYear()
var month = date.getMonth()
var day = date.getDate()
var firstDayOfMonth = new Date(year, month, 1)
var dayOfWeek = firstDayOfMonth.getDay()
var headerSize = (dayOfWeek===0?7:dayOfWeek)-1
if(headerSize!==0){
var lastMonthYear = year;
var lastMonthMonth = month - 1
if (month === 0) {
lastMonthYear = year - 1
lastMonthMonth = 11
}
var lastMonthDays = new Date(lastMonthYear, lastMonthMonth+1, 0).getDate()
for (var i = headerSize-1; i >= 0; i--) {
list_model.append(createItemDay(new Date(lastMonthYear, lastMonthMonth,lastMonthDays-i)))
}
}
const lastDayOfMonth = new Date(year, month+1, 0).getDate()
for (let day = 1; day <= lastDayOfMonth; day++) {
list_model.append(createItemDay(new Date(year, month,day)))
}
var footerSize = 49-list_model.count
var nextMonthYear = year
var nextMonth = month + 1
if (month === 11) {
nextMonthYear = year + 1
nextMonth = 0
}
const nextDayOfMonth = new Date(nextMonthYear, nextMonth+1, 0).getDate()
for (let j = 1; j <= footerSize; j++) {
list_model.append(createItemDay(new Date(nextMonthYear, nextMonth,j)))
}
title.text = year+"年"+(month+1)+"月"
}
}

View File

@ -0,0 +1,201 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
FluItem {
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
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
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)
}
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
}
Loader{
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.dark : Qt.rgba(1,1,1,0.7)
border.width: mouse_item.containsMouse ? 1 : 0
border.color: FluTheme.primaryColor.dark
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
Loader{
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: true
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
timer_run.restart()
}
}

View File

@ -0,0 +1,122 @@
import QtQuick 2.15
import "./../JS/Chart.js" as Chart
Canvas {
id: control
property var jsChart: undefined
property string chartType
property var chartData
property var chartOptions
property double chartAnimationProgress: 0.1
property int animationEasingType: Easing.InOutExpo
property double animationDuration: 500
property var memorizedContext
property var memorizedData
property var memorizedOptions
property alias animationRunning: chartAnimator.running
signal animationFinished()
function animateToNewData()
{
chartAnimationProgress = 0.1;
jsChart.update();
chartAnimator.restart();
}
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 && memorizedContext !== control.getContext('2d') || memorizedData !== control.chartData || memorizedOptions !== control.chartOptions) {
var ctx = control.getContext('2d');
jsChart = Chart.build(ctx, {
type: control.chartType,
data: control.chartData,
options: control.chartOptions
});
memorizedData = control.chartData ;
memorizedContext = control.getContext('2d');
memorizedOptions = control.chartOptions;
control.jsChart.bindEvents(function(newHandler) {event.handler = newHandler;});
chartAnimator.start();
}
jsChart.draw(chartAnimationProgress);
}
onWidthChanged: {
if(jsChart) {
jsChart.resize();
}
}
onHeightChanged: {
if(jsChart) {
jsChart.resize();
}
}
}

View File

@ -0,0 +1,120 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import FluentUI 1.0
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.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
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.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
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 var clickListener : function(){
checked = !checked
}
id:control
enabled: !disabled
onClicked: clickListener()
onCheckableChanged: {
if(checkable){
checkable = false
}
}
background: Item{
FluFocusRectangle{
radius: 4
visible: control.activeFocus
}
}
horizontalPadding:2
verticalPadding: 2
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: FluTheme.enableAnimation
ColorAnimation{
duration: 83
}
}
FluIcon {
anchors.centerIn: parent
iconSource: FluentIcons.AcceptMedium
iconSize: 15
visible: checked
iconColor: FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
Behavior on visible {
enabled: FluTheme.enableAnimation
NumberAnimation{
duration: 83
}
}
}
}
FluText{
id:btn_text
text: control.text
Layout.alignment: Qt.AlignVCenter
visible: text !== ""
}
}
}

View File

@ -0,0 +1,93 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.Window 2.15
import FluentUI 1.0
Button{
id:control
width: 36
height: 36
implicitWidth: width
implicitHeight: height
property alias colorValue: container.colorValue
background:
Rectangle{
id:layout_color
radius: 5
color:"#00000000"
border.color: {
if(hovered)
return FluTheme.primaryColor.light
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: container.colorValue
}
}
Item{
id: d
property var window : Window.window
}
contentItem: Item{}
onClicked: {
popup.showPopup()
}
Menu{
id:popup
modal: true
dim:false
height: container.height
width: container.width
contentItem: Item{
clip: true
FluColorView{
id:container
}
}
background:Item{
FluShadow{
radius: 5
}
}
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
}
}
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()
}
}
function setColor(color){
container.setColor(color)
}
}

View File

@ -0,0 +1,24 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
import "ColorPicker"
Item {
property alias colorValue: color_picker.colorValue
width: color_picker.width+10
height: color_picker.height
FluArea{
anchors.fill: parent
radius: 5
FluShadow{
radius: 5
}
ColorPicker{
id:color_picker
}
}
function setColor(color) {
color_picker.setColor(color)
}
}

View File

@ -0,0 +1,137 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
import QtQuick.Templates 2.15 as 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.lightest,0.6)
selectedTextColor: color
text: control.editable ? control.editText : control.displayText
enabled: control.editable
autoScroll: control.editable
font:control.font
readOnly: control.down
color: 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{
border.width: 0
inputItem: contentItem
}
Component.onCompleted: {
forceActiveFocus()
}
Keys.onEnterPressed: (event)=> handleCommit(event)
Keys.onReturnPressed:(event)=> handleCommit(event)
function handleCommit(event){
control.commit(control.editText)
}
}
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
contentItem: ListView {
clip: true
implicitHeight: contentHeight
model: control.delegateModel
currentIndex: control.highlightedIndex
highlightMoveDuration: 0
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
}
}
}
}

View File

@ -0,0 +1,129 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
import FluentUI 1.0
FluPopup {
id: popup
property string title: "Title"
property string message: "Message"
property string neutralText: "Neutral"
property string negativeText: "Negative"
property string positiveText: "Positive"
property int delayTime: 100
signal neutralClicked
signal negativeClicked
signal positiveClicked
property int buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
focus: true
implicitWidth: 400
implicitHeight: text_title.height + text_message.height + layout_actions.height
Rectangle {
id:layout_content
anchors.fill: parent
color: 'transparent'
radius:5
FluText{
id:text_title
font: FluTextStyle.TitleLarge
text:title
topPadding: 20
leftPadding: 20
rightPadding: 20
wrapMode: Text.WrapAnywhere
anchors{
top:parent.top
left: parent.left
right: parent.right
}
}
FluText{
id:text_message
font: FluTextStyle.Body
wrapMode: Text.WrapAnywhere
text:message
topPadding: 14
leftPadding: 20
rightPadding: 20
bottomPadding: 14
anchors{
top:text_title.bottom
left: parent.left
right: parent.right
}
}
Rectangle{
id:layout_actions
height: 68
radius: 5
color: FluTheme.dark ? Qt.rgba(32/255,32/255,32/255,1) : Qt.rgba(243/255,243/255,243/255,1)
anchors{
top:text_message.bottom
left: parent.left
right: parent.right
}
RowLayout{
anchors
{
centerIn: parent
margins: spacing
fill: parent
}
spacing: 15
FluButton{
id:neutral_btn
Layout.fillWidth: true
Layout.fillHeight: true
visible: popup.buttonFlags&FluContentDialogType.NeutralButton
text: neutralText
onClicked: {
popup.close()
timer_delay.targetFlags = FluContentDialogType.NeutralButton
timer_delay.restart()
}
}
FluButton{
id:negative_btn
Layout.fillWidth: true
Layout.fillHeight: true
visible: popup.buttonFlags&FluContentDialogType.NegativeButton
text: negativeText
onClicked: {
popup.close()
timer_delay.targetFlags = FluContentDialogType.NegativeButton
timer_delay.restart()
}
}
FluFilledButton{
id:positive_btn
Layout.fillWidth: true
Layout.fillHeight: true
visible: popup.buttonFlags&FluContentDialogType.PositiveButton
text: positiveText
onClicked: {
popup.close()
timer_delay.targetFlags = FluContentDialogType.PositiveButton
timer_delay.restart()
}
}
}
}
}
Timer{
property int targetFlags
id:timer_delay
interval: popup.delayTime
onTriggered: {
if(targetFlags === FluContentDialogType.NegativeButton){
negativeClicked()
}
if(targetFlags === FluContentDialogType.NeutralButton){
neutralClicked()
}
if(targetFlags === FluContentDialogType.PositiveButton){
positiveClicked()
}
}
}
}

View File

@ -0,0 +1,60 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
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
}
}
FluStatusView{
id:status_view
color: "#00000000"
statusMode: FluStatusViewType.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
}
}
}

View File

@ -0,0 +1,27 @@
import QtQuick 2.15
import FluentUI 1.0
import QtQuick.Templates 2.15 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()
}

View File

@ -0,0 +1,35 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
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.lightest,0.6)
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
}
}

View File

@ -0,0 +1,401 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.Window 2.15
import FluentUI 1.0
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
id:control
color: {
if(mouse_area.containsMouse){
return hoverColor
}
return normalColor
}
height: 30
width: 300
radius: 4
border.width: 1
border.color: dividerColor
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:"年"
}
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:"月"
}
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:"日"
}
Menu{
id:popup
modal: true
width: container.width
height: container.height
dim:false
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){
if(FluTheme.dark){
return item_mouse.containsMouse ? Qt.darker(FluTheme.primaryColor.lighter,1.1) : FluTheme.primaryColor.lighter
}else{
return item_mouse.containsMouse ? Qt.lighter(FluTheme.primaryColor.dark,1.1): FluTheme.primaryColor.dark
}
}
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: Loader{
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: Loader{
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: Loader{
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: "取消"
onClicked: {
popup.close()
}
}
FluFilledButton{
anchors{
right: parent.right
left: divider.right
rightMargin: 20
leftMargin: 10
verticalCenter: parent.verticalCenter
}
text: "确定"
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
}
}
}
}
}
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 === "年"? now.getFullYear() : Number(text_year.text);
var month = text_month.text === "月"? now.getMonth() + 1 : Number(text_month.text);
var day = text_day.text === "日" ? 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;
}
}

View File

@ -0,0 +1,18 @@
import QtQuick 2.15
import QtQuick.Window 2.15
import FluentUI 1.0
Rectangle {
property real spacing
property alias separatorHeight:separator.height
id:root
color:Qt.rgba(0,0,0,0)
height: spacing*2+separator.height
Rectangle{
id:separator
color: FluTheme.dark ? Qt.rgba(80/255,80/255,80/255,1) : Qt.rgba(210/255,210/255,210/255,1)
width:parent.width
anchors.centerIn: parent
}
}

View File

@ -0,0 +1,94 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
import FluentUI 1.0
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(251/255,251/255,251/255,1)
property color disableColor: FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(252/255,252/255,252/255,1)
property color 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
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
}
}

View File

@ -0,0 +1,122 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
import FluentUI 1.0
Item {
property string headerText: "Titlte"
property bool expand: false
property int contentHeight : 300
default property alias content: container.data
id:control
implicitHeight: Math.max((layout_header.height + container.height),layout_header.height)
implicitWidth: 400
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: {
expand = !expand
}
}
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: {
expand = !expand
}
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{
anchors{
top: layout_header.bottom
topMargin: -1
left: layout_header.left
}
width: parent.width
clip: true
visible: contentHeight+container.y !== 0
height: contentHeight+container.y
Rectangle{
id:container
width: parent.width
height: parent.height
radius: 4
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)
y: -contentHeight
states: [
State{
name:"expand"
when: control.expand
PropertyChanges {
target: container
y:0
}
},
State{
name:"collapsed"
when: !control.expand
PropertyChanges {
target: container
y:-contentHeight
}
}
]
transitions: [
Transition {
to:"expand"
NumberAnimation {
properties: "y"
duration: FluTheme.enableAnimation ? 167 : 0
easing.type: Easing.OutCubic
}
},
Transition {
to:"collapsed"
NumberAnimation {
properties: "y"
duration: FluTheme.enableAnimation ? 167 : 0
easing.type: Easing.OutCubic
}
}
]
}
}
}

View File

@ -0,0 +1,56 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
Button {
property bool disabled: false
property string contentDescription: ""
property color normalColor: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
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
horizontalPadding:12
background: Rectangle{
implicitWidth: 28
implicitHeight: 28
radius: 4
FluFocusRectangle{
visible: control.visualFocus
radius:4
}
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
}
}

View File

@ -0,0 +1,106 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
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)
}
}
}

View File

@ -0,0 +1,19 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
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
}
}

View File

@ -0,0 +1,19 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
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"
}
}

View File

@ -0,0 +1,140 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import FluentUI 1.0
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.dark ? Qt.rgba(1,1,1,0.03) : Qt.rgba(0,0,0,0.03)
property color pressedColor: FluTheme.dark ? Qt.rgba(1,1,1,0.06) : Qt.rgba(0,0,0,0.06)
property color normalColor: FluTheme.dark ? Qt.rgba(0,0,0,0) : Qt.rgba(0,0,0,0)
property color disableColor: FluTheme.dark ? Qt.rgba(0,0,0,0) : Qt.rgba(0,0,0,0)
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)
}
}
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
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{
Loader{
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
}
FluTooltip{
id:tool_tip
visible: {
if(control.text === ""){
return false
}
if(control.display !== Button.IconOnly){
return false
}
return hovered
}
text:control.text
delay: 1000
}
}
}
Component{
id:com_column
ColumnLayout{
Loader{
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
}
FluTooltip{
id:tool_tip
visible: {
if(control.text === ""){
return false
}
if(control.display !== Button.IconOnly){
return false
}
return hovered
}
text:control.text
delay: 1000
}
}
}
contentItem:Loader{
sourceComponent: {
if(display === Button.TextUnderIcon){
return com_column
}
return com_row
}
}
}

View File

@ -0,0 +1,48 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
Image {
property string errorButtonText: "重新加载"
property var clickErrorListener : function(){
image.source = ""
image.source = control.source
}
property Component errorItem : com_error
property Component loadingItem: com_loading
id: control
Loader{
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.dark ? Qt.rgba(1,1,1,0.03) : Qt.rgba(0,0,0,0.03)
FluProgressRing{
anchors.centerIn: parent
visible: control.status === Image.Loading
}
}
}
Component{
id:com_error
Rectangle{
color: FluTheme.dark ? Qt.rgba(1,1,1,0.03) : Qt.rgba(0,0,0,0.03)
FluFilledButton{
text: control.errorButtonText
anchors.centerIn: parent
visible: control.status === Image.Error
onClicked: clickErrorListener()
}
}
}
}

View File

@ -0,0 +1,210 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
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{
spacing: 20
width: parent.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: true; repeat: true
onTriggered: content.close();
}
Loader{
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 + (_super.moremsg ? 25 : 80);
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"
}
}
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.lighter;
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"
}
}
}
FluText{
text:_super.text
wrapMode: Text.WrapAnywhere
width: Math.min(implicitWidth,mcontrol.maxWidth)
}
}
}
}
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);
}
}

View File

@ -0,0 +1,57 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15
Item{
property var radius:[0,0,0,0]
default property alias contentItem: container.data
id:control
Item{
id:container
width: control.width
height: control.height
opacity: 0
}
onWidthChanged: {
canvas.requestPaint()
}
onHeightChanged: {
canvas.requestPaint()
}
onRadiusChanged: {
canvas.requestPaint()
}
Canvas {
id: canvas
anchors.fill: parent
visible: false
onPaint: {
var ctx = getContext("2d");
var x = 0;
var y = 0;
var w = control.width;
var h = control.height;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.beginPath();
ctx.moveTo(x + radius[0], y);
ctx.lineTo(x + w - radius[1], y);
ctx.arcTo(x + w, y, x + w, y + radius[1], radius[1]);
ctx.lineTo(x + w, y + h - radius[2]);
ctx.arcTo(x + w, y + h, x + w - radius[2], y + h, radius[2]);
ctx.lineTo(x + radius[3], y + h);
ctx.arcTo(x, y + h, x, y + h - radius[3], radius[3]);
ctx.lineTo(x, y + radius[0]);
ctx.arcTo(x, y, x + radius[0], y, radius[0]);
ctx.closePath();
ctx.fillStyle = control.color;
ctx.fill();
ctx.restore();
}
}
OpacityMask {
anchors.fill: container
source: container
maskSource: canvas
}
}

View File

@ -0,0 +1,38 @@
import QtQuick 2.15
import QtQuick.Templates 2.15 as T
import FluentUI 1.0
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: 12
spacing: 8
icon.width: 24
icon.height: 24
icon.color: control.palette.text
contentItem: FluText {
text: control.text
font: control.font
}
background: Rectangle {
implicitWidth: 100
implicitHeight: 40
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
}
}

View File

@ -0,0 +1,59 @@
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Templates 2.15 as T
import FluentUI 1.0
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: FluTools.colorAlpha(control.palette.shadow, 0.5)
}
T.Overlay.modeless: Rectangle {
color: FluTools.colorAlpha(control.palette.shadow, 0.12)
}
}

View File

@ -0,0 +1,20 @@
import QtQuick 2.15
import QtQuick.Templates 2.15 as T
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
}
}

View File

@ -0,0 +1,69 @@
import QtQuick 2.15
import QtQuick.Templates 2.15 as T
import FluentUI 1.0
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(FluTheme.dark){
if(control.highlighted){
return Qt.rgba(1,1,1,0.06)
}
if(control.hovered){
return Qt.rgba(1,1,1,0.03)
}
return Qt.rgba(0,0,0,0)
}else{
if(control.highlighted){
return Qt.rgba(0,0,0,0.06)
}
if(control.hovered){
return Qt.rgba(0,0,0,0.03)
}
return Qt.rgba(0,0,0,0)
}
}
}
}

View File

@ -0,0 +1,115 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.Templates 2.15 as T
import FluentUI 1.0
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
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
}
Loader{
id:loader_icon
sourceComponent: iconDelegate
anchors.verticalCenter: parent.verticalCenter
visible: status === Loader.Ready
}
FluText {
id:content_text
text: control.text
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(FluTheme.dark){
if(control.highlighted){
return Qt.rgba(1,1,1,0.06)
}
return Qt.rgba(0,0,0,0)
}else{
if(control.highlighted){
return Qt.rgba(0,0,0,0.06)
}
return Qt.rgba(0,0,0,0)
}
}
}
}
}

View File

@ -0,0 +1,18 @@
import QtQuick 2.15
import QtQuick.Templates 2.15 as T
import FluentUI 1.0
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)
}
}

View File

@ -0,0 +1,65 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
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)
id:control
enabled: !disabled
color: {
if(!enabled){
return disableColor
}
return normalColor
}
font:FluTextStyle.Body
wrapMode: Text.WrapAnywhere
padding: 8
leftPadding: padding+2
renderType: FluTheme.nativeText ? Text.NativeRendering : Text.QtRendering
selectedTextColor: color
selectionColor: FluTools.colorAlpha(FluTheme.primaryColor.lightest,0.6)
placeholderTextColor: {
if(!enabled){
return placeholderDisableColor
}
if(focus){
return placeholderFocusColor
}
return placeholderNormalColor
}
selectByMouse: true
width: background.implicitWidth
background: FluTextBoxBackground{
inputItem: control
implicitWidth: 240
}
Keys.onEnterPressed: (event)=> d.handleCommit(event)
Keys.onReturnPressed:(event)=> d.handleCommit(event)
QtObject{
id:d
function handleCommit(event){
if(event.modifiers & Qt.ControlModifier){
insert(control.cursorPosition, "\n")
return
}
control.commit(control.text)
}
}
MouseArea{
anchors.fill: parent
cursorShape: Qt.IBeamCursor
acceptedButtons: Qt.RightButton
onClicked: control.echoMode !== TextInput.Password && menu.popup()
}
FluTextBoxMenu{
id:menu
inputItem: control
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
QtObject {
default property list<QtObject> children
id:control
}

View File

@ -0,0 +1,34 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
import FluentUI 1.0
Item {
property int launchMode: FluPageType.SingleTop
property bool animDisabled: false
property string url : ""
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
}
}

View File

@ -0,0 +1,100 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import FluentUI 1.0
Item {
signal requestPage(int page,int count)
property string previousText: "<上一页"
property string nextText: "下一页>"
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)
}
}

View File

@ -0,0 +1,25 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
QtObject {
readonly property string key : FluTools.uuid()
property int _idx
property var _ext
property string title
property int order : 0
property int icon
property Component cusIcon
property Component infoBadge
property bool recentlyAdded: false
property bool recentlyUpdated: false
property string desc
property var image
property var parent
property int count: 0
signal tap
property var tapFunc
property Component menuDelegate
property Component editDelegate
property bool showEdit
}

View File

@ -0,0 +1,10 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
QtObject {
readonly property string key : FluTools.uuid()
property int _idx
property var _ext
property var parent
}

View File

@ -0,0 +1,16 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
FluObject {
readonly property string key : FluTools.uuid()
property int _idx
property string title
property var icon
property Component cusIcon
property bool isExpand: false
property var parent
property Component menuDelegate
property Component editDelegate
property bool showEdit
}

View File

@ -0,0 +1,10 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
QtObject {
readonly property string key : FluTools.uuid()
property int _idx
property string title
property var parent
}

View File

@ -0,0 +1,11 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
QtObject {
readonly property string key : FluTools.uuid()
property int _idx
property var parent
property real spacing
property int size:1
}

View File

@ -0,0 +1,84 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
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: 8
leftPadding: padding+2
echoMode:btn_reveal.pressed ? TextField.Normal : TextField.Password
renderType: FluTheme.nativeText ? Text.NativeRendering : Text.QtRendering
selectionColor: FluTools.colorAlpha(FluTheme.primaryColor.lightest,0.6)
selectedTextColor: color
placeholderTextColor: {
if(!enabled){
return placeholderDisableColor
}
if(focus){
return placeholderFocusColor
}
return placeholderNormalColor
}
selectByMouse: true
rightPadding: icon_end.visible ? 50 : 30
background: FluTextBoxBackground{
inputItem: control
implicitWidth: 240
FluIcon{
id:icon_end
iconSource: control.iconSource
iconSize: 15
opacity: 0.5
visible: control.iconSource != 0
anchors{
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: 5
}
}
}
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: 20
height: 20
verticalPadding: 0
horizontalPadding: 0
opacity: 0.5
visible: control.text !== ""
anchors{
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: icon_end.visible ? 25 : 5
}
}
FluTextBoxMenu{
id:menu
inputItem: control
}
}

View File

@ -0,0 +1,92 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
Item {
default property alias content: d.children
property alias currentIndex: nav_list.currentIndex
property color normalColor: FluTheme.dark ? FluColors.Grey120 : FluColors.Grey120
property color hoverColor: FluTheme.dark ? FluColors.Grey10 : FluColors.Black
id:control
width: 400
height: 300
implicitHeight: height
implicitWidth: width
MouseArea{
anchors.fill: parent
preventStealing: true
}
FluObject{
id:d
}
ListView{
id:nav_list
height: 40
width: control.width
model:d.children
clip: true
spacing: 20
interactive: false
orientation: ListView.Horizontal
highlightMoveDuration: FluTheme.enableAnimation ? 167 : 0
highlight: Item{
clip: true
Rectangle{
height: 3
radius: 1.5
color: FluTheme.primaryColor.dark
width: nav_list.currentItem ? nav_list.currentItem.width : 0
y:37
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
background:Item{
}
contentItem: Item{
FluText {
id:item_title
font: FluTextStyle.Title
text: modelData.title
anchors.centerIn: parent
color: {
if(item_button.hovered)
return hoverColor
return normalColor
}
}
}
onClicked: {
nav_list.currentIndex = index
}
}
}
Item{
id:container
anchors{
top: nav_list.bottom
topMargin: 10
left: parent.left
right: parent.right
bottom: parent.bottom
}
Repeater{
model:d.children
Loader{
property var argument: modelData.argument
anchors.fill: parent
sourceComponent: modelData.contentItem
visible: nav_list.currentIndex === index
}
}
}
}

View File

@ -0,0 +1,9 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
QtObject {
property string title
property Component contentItem
property var argument
}

View File

@ -0,0 +1,47 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
import FluentUI 1.0
Popup {
id: popup
padding: 0
modal:true
anchors.centerIn: Overlay.overlay
closePolicy: Popup.CloseOnEscape
enter: Transition {
NumberAnimation {
properties: "scale"
from:1.2
to:1
duration: FluTheme.enableAnimation ? 83 : 0
easing.type: Easing.OutCubic
}
NumberAnimation {
property: "opacity"
duration: FluTheme.enableAnimation ? 83 : 0
from:0
to:1
}
}
exit:Transition {
NumberAnimation {
properties: "scale"
from:1
to:1.2
duration: FluTheme.enableAnimation ? 83 : 0
easing.type: Easing.OutCubic
}
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)
}
}

View File

@ -0,0 +1,60 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
ProgressBar{
property real strokeWidth: 6
property bool progressVisible: false
property color color: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
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
}
background: Rectangle {
implicitWidth: 150
implicitHeight: control.strokeWidth
color: control.backgroundColor
radius: d._radius
}
contentItem: FluItem {
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 {
running: control.indeterminate && control.visible
from: -rect_progress.width
to:control.width+rect_progress.width
loops: Animation.Infinite
duration: 888
}
}
}
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
}
}
}

View File

@ -0,0 +1,74 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Shapes 1.15
import FluentUI 1.0
ProgressBar{
property real strokeWidth: 6
property bool progressVisible: false
property color color: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
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
}
QtObject{
id:d
property real _radius: control.width/2-control.strokeWidth/2
property real _progress: control.indeterminate ? 0.3 : control.visualPosition
on_ProgressChanged: {
canvas.requestPaint()
}
}
Connections{
target: FluTheme
function onDarkChanged(){
canvas.requestPaint()
}
}
contentItem: Item {
RotationAnimation on rotation {
running: control.indeterminate && control.visible
from: 0
to:360
loops: Animation.Infinite
duration: 888
}
Canvas {
id:canvas
anchors.fill: parent
antialiasing: true
renderTarget: Canvas.Image
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.beginPath()
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
}
}

View File

@ -0,0 +1,23 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
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
}
QRCode{
id:qrcode
size:control.size-margins
anchors.centerIn: parent
}
}

View File

@ -0,0 +1,98 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import FluentUI 1.0
Button {
property string contentDescription: ""
property bool disabled: false
property color borderNormalColor: checked ? FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark : 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()
onCheckableChanged: {
if(checkable){
checkable = false
}
}
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
}
}
}

View File

@ -0,0 +1,29 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import FluentUI 1.0
ColumnLayout {
default property alias buttons: control.data
property int currentIndex : -1
id:control
onCurrentIndexChanged: {
for(var i = 0;i<buttons.length;i++){
buttons[i].checked = false
}
buttons[currentIndex].checked = true
}
Component.onCompleted: {
for(var i = 0;i<buttons.length;i++){
buttons[i].clickListener = function(){
for(var i = 0;i<buttons.length;i++){
var button = buttons[i]
if(this === button){
currentIndex = i
}
}
}
}
currentIndex = 0
}
}

View File

@ -0,0 +1,54 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
Item {
property int number: 5
property int spacing: 4
property int size: 18
property int value:0
id:control
implicitWidth: container.width
implicitHeight: container.height
QtObject{
id:d
property int mouseValue: 0
property int itemSize: control.size+spacing*2
}
Row{
id:container
spacing: 0
Repeater{
model:control.number
Item{
width: d.itemSize
height: d.itemSize
FluIcon{
property bool isSelected : {
if(d.mouseValue!==0){
return index<d.mouseValue
}
return index<control.value
}
iconSize: control.size
iconSource: isSelected ? FluentIcons.FavoriteStarFill : FluentIcons.FavoriteStar
iconColor: isSelected ? FluTheme.primaryColor.dark : (FluTheme.dark ? "#FFFFFF" : "#000000")
anchors.centerIn: parent
}
}
}
}
MouseArea{
anchors.fill: container
hoverEnabled: true
onPositionChanged: (mouse)=>{
d.mouseValue = Number(mouse.x / d.itemSize)+1
}
onExited: {
d.mouseValue = 0
}
onClicked: (mouse)=>{
control.value = Number(mouse.x / d.itemSize)+1
}
}
}

View File

@ -0,0 +1,71 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15
import FluentUI 1.0
Item{
property var radius:[0,0,0,0]
property color color : FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
property bool shadow: true
default property alias contentItem: container.data
id:control
onWidthChanged: {
canvas.requestPaint()
}
onHeightChanged: {
canvas.requestPaint()
}
onRadiusChanged: {
canvas.requestPaint()
}
FluShadow{
anchors.fill: container
radius: control.radius[0]
visible: {
if(control.radius[0] === control.radius[1] && control.radius[0] === control.radius[2] && control.radius[0] === control.radius[3] && control.shadow){
return true
}
return false
}
}
Rectangle{
id:container
width: control.width
height: control.height
opacity: 0
color:control.color
}
Canvas {
id: canvas
anchors.fill: parent
visible: false
onPaint: {
var ctx = getContext("2d")
var x = 0
var y = 0
var w = control.width
var h = control.height
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.save()
ctx.beginPath();
ctx.moveTo(x + radius[0], y)
ctx.lineTo(x + w - radius[1], y)
ctx.arcTo(x + w, y, x + w, y + radius[1], radius[1])
ctx.lineTo(x + w, y + h - radius[2])
ctx.arcTo(x + w, y + h, x + w - radius[2], y + h, radius[2])
ctx.lineTo(x + radius[3], y + h)
ctx.arcTo(x, y + h, x, y + h - radius[3], radius[3])
ctx.lineTo(x, y + radius[0])
ctx.arcTo(x, y, x + radius[0], y, radius[0])
ctx.closePath()
ctx.fillStyle = "#000000"
ctx.fill()
ctx.restore()
}
}
OpacityMask {
anchors.fill: container
source: container
maskSource: canvas
}
}

View File

@ -0,0 +1,39 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
FluStatusView {
property url source: ""
property bool lazy: false
color:"transparent"
id:control
onErrorClicked: {
reload()
}
Component.onCompleted: {
if(!lazy){
loader.source = control.source
}
}
Loader{
id:loader
anchors.fill: parent
asynchronous: true
onStatusChanged: {
if(status === Loader.Error){
control.statusMode = FluStatusViewType.Error
}else if(status === Loader.Loading){
control.statusMode = FluStatusViewType.Loading
}else{
control.statusMode = FluStatusViewType.Success
}
}
}
function reload(){
var timestamp = Date.now();
loader.source = control.source+"?"+timestamp
}
function itemLodaer(){
return loader
}
}

View File

@ -0,0 +1,523 @@
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Qt.labs.platform 1.1
import FluentUI 1.0
Item{
id:control
property int captrueMode: FluScreenshotType.Pixmap
property int dotSize: 5
property int borderSize: 1
property var saveFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
property color borderColor: FluTheme.primaryColor.dark
signal captrueCompleted(var captrue)
QtObject{
id:d
property int dotMouseSize: control.dotSize+10
property int dotMargins: -(control.dotSize-control.borderSize)/2
property bool enablePosition: false
property int menuMargins: 6
}
Loader {
id:loader
}
Component{
id:com_screen
Window{
property bool isZeroPos: screenshot.start === Qt.point(0,0) && screenshot.end === Qt.point(0,0)
id:window_screen
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint
x:-1
y:-1
width: 1
height: 1
visible: true
color: "#00000000"
onVisibleChanged: {
if(!window_screen.visible){
loader.sourceComponent = undefined
}
}
Component.onCompleted: {
setGeometry(0,0,screenshot_background.width,screenshot_background.height)
}
ScreenshotBackground{
id:screenshot_background
captureMode:control.captrueMode
saveFolder: {
if(typeof control.saveFolder === 'string'){
return control.saveFolder
}else{
return FluTools.toLocalPath(control.saveFolder)
}
}
onCaptrueToPixmapCompleted:
(captrue)=>{
control.captrueCompleted(captrue)
loader.sourceComponent = undefined
}
onCaptrueToFileCompleted:
(captrue)=>{
control.captrueCompleted(captrue)
loader.sourceComponent = undefined
}
}
Screenshot{
id:screenshot
anchors.fill: parent
}
MouseArea{
anchors.fill: parent
acceptedButtons: Qt.RightButton | Qt.LeftButton
onPressed:
(mouse)=>{
if(mouse.button === Qt.LeftButton){
d.enablePosition = true
screenshot.start = Qt.point(mouse.x,mouse.y)
screenshot.end = Qt.point(mouse.x,mouse.y)
}
}
onPositionChanged:
(mouse)=>{
if(d.enablePosition){
screenshot.end = Qt.point(mouse.x,mouse.y)
}
}
onReleased:
(mouse)=>{
if(mouse.button === Qt.LeftButton){
d.enablePosition = false
screenshot.end = Qt.point(mouse.x,mouse.y)
}
}
onCanceled:
(mouse)=>{
if(mouse.button === Qt.LeftButton){
d.enablePosition = false
screenshot.end = Qt.point(mouse.x,mouse.y)
}
}
onClicked:
(mouse)=>{
if (mouse.button === Qt.RightButton){
if(isZeroPos){
loader.sourceComponent = undefined
return
}
screenshot.start = Qt.point(0,0)
screenshot.end = Qt.point(0,0)
}
}
}
Rectangle{
id:rect_capture
x:Math.min(screenshot.start.x,screenshot.end.x)
y:Math.min(screenshot.start.y,screenshot.end.y)
width: Math.abs(screenshot.end.x - screenshot.start.x)
height: Math.abs(screenshot.end.y - screenshot.start.y)
color:"#00000000"
border.width: control.borderSize
border.color: control.borderColor
MouseArea{
property point clickPos: Qt.point(0,0)
anchors.fill: parent
cursorShape: Qt.SizeAllCursor
onPressed:
(mouse)=>{
clickPos = Qt.point(mouse.x, mouse.y)
}
onPositionChanged:
(mouse)=>{
var delta = Qt.point(mouse.x - clickPos.x,mouse.y - clickPos.y)
var w = Math.abs(screenshot.end.x - screenshot.start.x)
var h = Math.abs(screenshot.end.y - screenshot.start.y)
var x = Math.min(Math.max(rect_capture.x + delta.x,0),window_screen.width-w)
var y =Math.min(Math.max(rect_capture.y + delta.y,0),window_screen.height-h)
screenshot.start = Qt.point(x,y)
screenshot.end = Qt.point(x+w,y+h)
}
}
}
Rectangle{
id:rect_top_left
width: control.dotSize
height: control.dotSize
color: control.borderColor
visible: !isZeroPos
anchors{
left: rect_capture.left
leftMargin: d.dotMargins
topMargin: d.dotMargins
top: rect_capture.top
}
}
MouseArea{
cursorShape: Qt.SizeFDiagCursor
anchors.centerIn: rect_top_left
width: d.dotMouseSize
height: d.dotMouseSize
visible: !isZeroPos
onPressed:
(mouse)=> {
FluTools.setOverrideCursor(cursorShape)
var x = rect_capture.x
var y = rect_capture.y
var w = rect_capture.width
var h = rect_capture.height
screenshot.start = Qt.point(x+w,y+h)
screenshot.end = Qt.point(x,y)
}
onReleased:
(mouse)=> {
FluTools.restoreOverrideCursor()
}
onPositionChanged:
(mouse)=> {
screenshot.end = mapToItem(screenshot,Qt.point(mouse.x,mouse.y))
}
onCanceled:
(mouse)=> {
FluTools.restoreOverrideCursor()
}
}
Rectangle{
id:rect_top_center
width: control.dotSize
height: control.dotSize
color: control.borderColor
visible: !isZeroPos
anchors{
horizontalCenter: rect_capture.horizontalCenter
topMargin: d.dotMargins
top: rect_capture.top
}
}
MouseArea{
cursorShape: Qt.SizeVerCursor
anchors.centerIn: rect_top_center
width: d.dotMouseSize
height: d.dotMouseSize
visible: !isZeroPos
onPressed:
(mouse)=> {
FluTools.setOverrideCursor(cursorShape)
var x = rect_capture.x
var y = rect_capture.y
var w = rect_capture.width
var h = rect_capture.height
screenshot.start = Qt.point(x+w,y+h)
screenshot.end = Qt.point(x,y)
}
onReleased:
(mouse)=> {
FluTools.restoreOverrideCursor()
}
onPositionChanged:
(mouse)=> {
var x = rect_capture.x
screenshot.end = Qt.point(x,mapToItem(screenshot,Qt.point(mouse.x,mouse.y)).y)
}
onCanceled:
(mouse)=> {
FluTools.restoreOverrideCursor()
}
}
Rectangle{
id:rect_top_right
width: control.dotSize
height: control.dotSize
color: control.borderColor
visible: !isZeroPos
anchors{
right: rect_capture.right
rightMargin: d.dotMargins
topMargin: d.dotMargins
top: rect_capture.top
}
}
MouseArea{
cursorShape: Qt.SizeBDiagCursor
anchors.centerIn: rect_top_right
width: d.dotMouseSize
height: d.dotMouseSize
visible: !isZeroPos
onPressed:
(mouse)=> {
var x = rect_capture.x
var y = rect_capture.y
var w = rect_capture.width
var h = rect_capture.height
screenshot.start = Qt.point(x,y+h)
screenshot.end = Qt.point(x+w,y)
}
onReleased:
(mouse)=> {
FluTools.restoreOverrideCursor()
}
onPositionChanged:
(mouse)=> {
screenshot.end = mapToItem(screenshot,Qt.point(mouse.x,mouse.y))
}
onCanceled:
(mouse)=> {
FluTools.restoreOverrideCursor()
}
}
Rectangle{
id:rect_right_center
width: control.dotSize
height: control.dotSize
color: control.borderColor
visible: !isZeroPos
anchors{
right: rect_capture.right
rightMargin: d.dotMargins
verticalCenter: rect_capture.verticalCenter
}
}
MouseArea{
cursorShape: Qt.SizeHorCursor
anchors.centerIn: rect_right_center
width: d.dotMouseSize
height: d.dotMouseSize
visible: !isZeroPos
onPressed:
(mouse)=> {
var x = rect_capture.x
var y = rect_capture.y
var w = rect_capture.width
var h = rect_capture.height
screenshot.start = Qt.point(x,y)
screenshot.end = Qt.point(x+w,y+h)
}
onReleased:
(mouse)=> {
FluTools.restoreOverrideCursor()
}
onPositionChanged:
(mouse)=> {
var y = rect_capture.y
var h = rect_capture.height
screenshot.end = Qt.point(mapToItem(screenshot,Qt.point(mouse.x,mouse.y)).x,y+h)
}
onCanceled:
(mouse)=> {
FluTools.restoreOverrideCursor()
}
}
Rectangle{
id:rect_right_bottom
width: control.dotSize
height: control.dotSize
color: control.borderColor
visible: !isZeroPos
anchors{
right: rect_capture.right
rightMargin: d.dotMargins
bottom: rect_capture.bottom
bottomMargin: d.dotMargins
}
}
MouseArea{
cursorShape: Qt.SizeFDiagCursor
anchors.centerIn: rect_right_bottom
width: d.dotMouseSize
height: d.dotMouseSize
visible: !isZeroPos
onPressed:
(mouse)=> {
var x = rect_capture.x
var y = rect_capture.y
var w = rect_capture.width
var h = rect_capture.height
screenshot.start = Qt.point(x,y)
screenshot.end = Qt.point(x+w,y+h)
}
onReleased:
(mouse)=> {
FluTools.restoreOverrideCursor()
}
onPositionChanged:
(mouse)=> {
screenshot.end = mapToItem(screenshot,Qt.point(mouse.x,mouse.y))
}
onCanceled:
(mouse)=> {
FluTools.restoreOverrideCursor()
}
}
Rectangle{
id:rect_bottom_center
width: control.dotSize
height: control.dotSize
color: control.borderColor
visible: !isZeroPos
anchors{
horizontalCenter: rect_capture.horizontalCenter
bottom: rect_capture.bottom
bottomMargin: d.dotMargins
}
}
MouseArea{
cursorShape: Qt.SizeVerCursor
anchors.centerIn: rect_bottom_center
width: d.dotMouseSize
height: d.dotMouseSize
visible: !isZeroPos
onPressed:
(mouse)=> {
var x = rect_capture.x
var y = rect_capture.y
var w = rect_capture.width
var h = rect_capture.height
screenshot.start = Qt.point(x,y)
screenshot.end = Qt.point(x+w,y+h)
}
onReleased:
(mouse)=> {
FluTools.restoreOverrideCursor()
}
onPositionChanged:
(mouse)=> {
var x = rect_capture.x
var w = rect_capture.width
screenshot.end = Qt.point(x+w,mapToItem(screenshot,Qt.point(mouse.x,mouse.y)).y)
}
onCanceled:
(mouse)=> {
FluTools.restoreOverrideCursor()
}
}
Rectangle{
id:rect_bottom_left
width: control.dotSize
height: control.dotSize
color: control.borderColor
visible: !isZeroPos
anchors{
left: rect_capture.left
leftMargin: d.dotMargins
bottom: rect_capture.bottom
bottomMargin: d.dotMargins
}
}
MouseArea{
cursorShape: Qt.SizeBDiagCursor
anchors.centerIn: rect_bottom_left
width: d.dotMouseSize
height: d.dotMouseSize
visible: !isZeroPos
onPressed:
(mouse)=> {
var x = rect_capture.x
var y = rect_capture.y
var w = rect_capture.width
var h = rect_capture.height
screenshot.start = Qt.point(x+w,y)
screenshot.end = Qt.point(x,y+h)
}
onReleased:
(mouse)=> {
FluTools.restoreOverrideCursor()
}
onPositionChanged:
(mouse)=> {
screenshot.end = mapToItem(screenshot,Qt.point(mouse.x,mouse.y))
}
onCanceled:
(mouse)=> {
FluTools.restoreOverrideCursor()
}
}
Rectangle{
id:rect_left_center
width: control.dotSize
height: control.dotSize
color: control.borderColor
visible: !isZeroPos
anchors{
left: rect_capture.left
leftMargin: d.dotMargins
verticalCenter: rect_capture.verticalCenter
}
}
MouseArea{
cursorShape: Qt.SizeHorCursor
anchors.centerIn: rect_left_center
width: d.dotMouseSize
height: d.dotMouseSize
visible: !isZeroPos
onPressed:
(mouse)=> {
var x = rect_capture.x
var y = rect_capture.y
var w = rect_capture.width
var h = rect_capture.height
screenshot.start = Qt.point(x+w,y)
screenshot.end = Qt.point(x,y+h)
}
onReleased:
(mouse)=> {
FluTools.restoreOverrideCursor()
}
onPositionChanged:
(mouse)=> {
var y = rect_capture.y
var h = rect_capture.height
screenshot.end = Qt.point(mapToItem(screenshot,Qt.point(mouse.x,mouse.y)).x,y+h)
}
onCanceled:
(mouse)=> {
FluTools.restoreOverrideCursor()
}
}
Pane{
width: 100
height: 40
visible: {
if(screenshot.start === Qt.point(0,0) && screenshot.end === Qt.point(0,0)){
return false
}
if(d.enablePosition){
return false
}
return true
}
x:rect_capture.x + rect_capture.width - width
y:{
if(rect_capture.y + rect_capture.height + d.menuMargins < screenshot.height-height){
return rect_capture.y + rect_capture.height + d.menuMargins
}else if(rect_capture.y - height - d.menuMargins > 0){
return rect_capture.y - height - d.menuMargins
}else{
screenshot.height - height - d.menuMargins
}
}
RowLayout{
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
FluIconButton{
iconSource: FluentIcons.Cancel
iconSize: 18
iconColor: Qt.rgba(247/255,75/255,77/255,1)
onClicked: {
loader.sourceComponent = undefined
}
}
FluIconButton{
iconSource: FluentIcons.AcceptMedium
iconColor: FluTheme.primaryColor.dark
onClicked: {
screenshot_background.capture(screenshot.start,screenshot.end)
}
}
}
}
}
}
function open(){
loader.sourceComponent = com_screen
}
}

View File

@ -0,0 +1,85 @@
import QtQuick 2.15
import QtQuick.Templates 2.15 as T
import FluentUI 1.0
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)
padding: 2
visible: control.policy !== T.ScrollBar.AlwaysOff
minimumSize: Math.max(orientation === Qt.Horizontal ? height / width : width / height,0.3)
contentItem: Item {
property bool collapsed: (control.policy === T.ScrollBar.AlwaysOn || (control.active && control.size < 1.0))
implicitWidth: control.interactive ? 6 : 2
implicitHeight: control.interactive ? 6 : 2
Rectangle{
id:rect_bar
width: vertical ? 2 : parent.width
height: horizontal ? 2 : 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 ? 6 : parent.width
height: horizontal ? 6 : parent.height
}
}
,State{
name:"hide"
when: !contentItem.collapsed
PropertyChanges {
target: rect_bar
width: vertical ? 2 : parent.width
height: horizontal ? 2 : 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"
NumberAnimation {
target: rect_bar
properties: vertical ? "width" : "height"
duration: 167
easing.type: Easing.OutCubic
}
}
]
}
}

View File

@ -0,0 +1,48 @@
import QtQuick 2.15
import QtQuick.Templates 2.15 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
}
}
}
]
}
}

View File

@ -0,0 +1,79 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
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
}
}
FluStatusView{
id:status_view
color: "#00000000"
statusMode: FluStatusViewType.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
}
}
}
}

View File

@ -0,0 +1,66 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
Item {
property color color: FluTheme.dark ? "#FFFFFF" : "#999999"
property int radius: 4
id:control
anchors.fill: parent
anchors.margins: -4
Rectangle{
width: control.width
height: control.height
anchors.centerIn: parent
color: "#00000000"
opacity: 0.02
border.width: 1
radius: control.radius
border.color: control.color
}
Rectangle{
width: control.width - 2
height: control.height - 2
anchors.centerIn: parent
color: "#00000000"
opacity: 0.04
border.width: 1
radius: control.radius
border.color: control.color
}
Rectangle{
width: control.width - 4
height: control.height - 4
anchors.centerIn: parent
color: "#00000000"
opacity: 0.06
border.width: 1
radius: control.radius
border.color: control.color
}
Rectangle{
width: control.width - 6
height: control.height - 6
anchors.centerIn: parent
color: "#00000000"
opacity: 0.08
border.width: 1
radius: control.radius
border.color: control.color
}
Rectangle{
width: control.width - 8
height: control.height - 8
anchors.centerIn: parent
opacity: 0.1
radius: control.radius
color: "#00000000"
border.width: 1
border.color: control.color
}
}

View File

@ -0,0 +1,73 @@
import QtQuick 2.15
import QtQuick.Templates 2.15 as T
import FluentUI 1.0
T.Slider {
property bool tooltipEnabled: true
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: 24
implicitHeight: 24
radius: 12
color:FluTheme.dark ? Qt.rgba(69/255,69/255,69/255,1) :Qt.rgba(1,1,1,1)
FluShadow{
radius: 12
}
Rectangle{
width: 24
height: 24
radius: 12
color:FluTheme.dark ? FluTheme.primaryColor.lighter :FluTheme.primaryColor.dark
anchors.centerIn: parent
scale: {
if(control.pressed){
return 4/10
}
return control.hovered ? 6/10 : 5/10
}
Behavior on scale {
enabled: FluTheme.enableAnimation
NumberAnimation{
duration: 167
easing.type: Easing.OutCubic
}
}
}
}
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.dark ? FluTheme.primaryColor.lighter :FluTheme.primaryColor.dark
}
}
FluTooltip{
parent: control.handle
visible: control.tooltipEnabled && control.pressed
text:String(control.value)
}
}

View File

@ -0,0 +1,156 @@
import QtQuick 2.15
import QtQuick.Templates 2.15 as T
import FluentUI 1.0
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.lightest,0.6)
selectedTextColor: color
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
readOnly: !control.editable
validator: control.validator
inputMethodHints: control.inputMethodHints
Rectangle{
width: parent.width
height: contentItem.activeFocus ? 3 : 1
anchors.bottom: parent.bottom
visible: contentItem.enabled
color: {
if(FluTheme.dark){
contentItem.activeFocus ? FluTheme.primaryColor.lighter : Qt.rgba(166/255,166/255,166/255,1)
}else{
return contentItem.activeFocus ? FluTheme.primaryColor.dark : Qt.rgba(183/255,183/255,183/255,1)
}
}
Behavior on height{
enabled: FluTheme.enableAnimation
NumberAnimation{
duration: 83
easing.type: Easing.OutCubic
}
}
}
}
up.indicator: FluItem {
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: FluItem {
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)
}
}
}

View File

@ -0,0 +1,116 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import FluentUI 1.0
Item{
id:control
default property alias content: container.data
property int statusMode: FluStatusViewType.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===FluStatusViewType.Success
}
Loader{
id:loader
anchors.fill: parent
visible: statusMode!==FluStatusViewType.Success
sourceComponent: {
if(statusMode === FluStatusViewType.Loading){
return loadingItem
}
if(statusMode === FluStatusViewType.Empty){
return emptyItem
}
if(statusMode === FluStatusViewType.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 = FluStatusViewType.Success
}
function showLoadingView(){
statusMode = FluStatusViewType.Loading
}
function showEmptyView(){
statusMode = FluStatusViewType.Empty
}
function showErrorView(){
statusMode = FluStatusViewType.Error
}
}

View File

@ -0,0 +1,307 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import FluentUI 1.0
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
FluItem{
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)
}
radius: [6,6,0,0]
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)
var firstIdx = tab_nav.indexAt(tab_nav.contentX+1, pos.y)
var lastIdx = tab_nav.indexAt(tab_nav.width+tab_nav.contentX-1, pos.y)
if(lastIdx === -1){
lastIdx = tab_nav.count-1
}
if (idx!==-1 && idx >= firstIdx && idx <= lastIdx && d.dragIndex !== idx) {
tab_model.move(d.dragIndex, idx, 1)
d.dragIndex = idx;
}
}
}
Rectangle{
anchors.fill: parent
color: {
if(FluTheme.dark){
if(item_mouse_hove.containsMouse || item_btn_close.hovered){
return Qt.rgba(1,1,1,0.03)
}
if(tab_nav.currentIndex === index){
return Qt.rgba(1,1,1,0.06)
}
return Qt.rgba(0,0,0,0)
}else{
if(item_mouse_hove.containsMouse || item_btn_close.hovered){
return Qt.rgba(0,0,0,0.03)
}
if(tab_nav.currentIndex === index){
return Qt.rgba(0,0,0,0.06)
}
return Qt.rgba(0,0,0,0)
}
}
}
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.Nerver)
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
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
Loader{
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
}
}

View File

@ -0,0 +1,5 @@
import Qt.labs.qmlmodels 1.0
TableModelColumn{
}

View File

@ -0,0 +1,531 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Qt.labs.qmlmodels 1.0
import FluentUI 1.0
Rectangle {
property var columnSource
property var dataSource
property color selectionColor: FluTools.colorAlpha(FluTheme.primaryColor.lightest,0.6)
property color hoverButtonColor: FluTools.colorAlpha(selectionColor,0.2)
property color pressedButtonColor: FluTools.colorAlpha(selectionColor,0.4)
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 com_column = Qt.createComponent("FluTableModelColumn.qml")
if (com_column.status === Component.Ready) {
var columns= []
var header_rows = {}
columnSource.forEach(function(item){
var column = com_column.createObject(table_model,{display:item.dataIndex});
columns.push(column)
header_rows[item.dataIndex] = item.title
})
table_model.columns = columns
header_model.columns = columns
d.header_rows = [header_rows]
}
}
}
QtObject{
id:d
property int defaultItemWidth: 100
property int defaultItemHeight: 42
property var header_rows:[]
property bool selectionFlag: true
function obtEditDelegate(column,row){
var display = table_model.data(table_model.index(row,column),"display")
var cellItem = table_view.itemAtCell(column, row)
var cellPosition = cellItem.mapToItem(scroll_table, 0, 0)
item_loader.column = column
item_loader.row = row
item_loader.x = table_view.contentX + cellPosition.x
item_loader.y = table_view.contentY + cellPosition.y
item_loader.width = table_view.columnWidthProvider(column)
item_loader.height = table_view.rowHeightProvider(row)
item_loader.display = display
var obj =columnSource[column].editDelegate
if(obj){
return obj
}
if(columnSource[column].editMultiline === true){
return com_edit_multiline
}
return com_edit
}
}
onDataSourceChanged: {
table_model.clear()
dataSource.forEach(function(item){
table_model.appendRow(item)
})
}
TableModel {
id:table_model
}
Component{
id:com_edit
FluTextBox{
text: display
readOnly: true === columnSource[column].readOnly
Component.onCompleted: {
forceActiveFocus()
selectAll()
}
onCommit: {
if(!readOnly){
display = 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 === columnSource[column].readOnly
verticalAlignment: TextInput.AlignVCenter
Component.onCompleted: {
forceActiveFocus()
selectAll()
}
rightPadding: 24
onCommit: {
if(!readOnly){
display = 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: itemData
anchors.fill: parent
anchors.margins: 10
elide: Text.ElideRight
wrapMode: Text.WrapAnywhere
verticalAlignment: Text.AlignVCenter
HoverHandler{
id: hover_handler
}
FluTooltip{
text: item_text.text
delay: 500
visible: item_text.contentWidth < item_text.implicitWidth && item_text.contentHeight < item_text.implicitHeight && hover_handler.hovered
}
}
}
ScrollView{
id:scroll_table
anchors.left: header_vertical.right
anchors.top: header_horizontal.bottom
anchors.right: parent.right
anchors.bottom: parent.bottom
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
TableView {
id:table_view
ListModel{
id:model_columns
}
boundsBehavior: Flickable.StopAtBounds
ScrollBar.horizontal: FluScrollBar{
id:scroll_bar_h
}
ScrollBar.vertical: FluScrollBar{
id:scroll_bar_v
}
columnWidthProvider: function(column) {
var w = columnSource[column].width
if(!w){
w = columnSource[column].minimumWidth
}
if(!w){
w = d.defaultItemWidth
}
if(column === item_loader.column){
item_loader.width = w
}
if(column === item_loader.column-1){
let cellItem = table_view.itemAtCell(item_loader.column, item_loader.row)
if(cellItem){
let cellPosition = cellItem.mapToItem(scroll_table, 0, 0)
item_loader.x = table_view.contentX + cellPosition.x
}
}
return w
}
rowHeightProvider: function(row) {
if(row>=table_model.rowCount){
return 0
}
var h = table_model.getRow(row).height
if(!h){
h = table_model.getRow(row).minimumHeight
}
if(!h){
return d.defaultItemHeight
}
if(row === item_loader.row){
item_loader.height = h
}
if(row === item_loader.row-1){
let cellItem = table_view.itemAtCell(item_loader.column, item_loader.row)
if(cellItem){
let cellPosition = cellItem.mapToItem(scroll_table, 0, 0)
item_loader.y = table_view.contentY + cellPosition.y
}
}
return h
}
model: table_model
clip: true
delegate: Rectangle {
id:item_table
property var position: Qt.point(column,row)
color: (row%2!==0) ? control.color : (FluTheme.dark ? Qt.rgba(1,1,1,0.06) : Qt.rgba(0,0,0,0.06))
implicitHeight: 40
implicitWidth: {
var w = columnSource[column].width
if(!w){
w = columnSource[column].minimumWidth
}
if(!w){
w = d.defaultItemWidth
}
return w
}
Rectangle{
anchors.fill: parent
visible: !item_loader.sourceComponent
color: "#00000000"
}
MouseArea{
anchors.fill: parent
acceptedButtons: Qt.LeftButton
onPressed:{
closeEditor()
table_view.interactive = false
}
onCanceled: {
table_view.interactive = true
}
onReleased: {
table_view.interactive = true
}
onDoubleClicked:{
if(display instanceof Component){
return
}
item_loader.sourceComponent = d.obtEditDelegate(column,row)
}
onClicked:
(event)=>{
item_loader.sourceComponent = undefined
d.selectionFlag = !d.selectionFlag
event.accepted = true
}
}
Loader{
property var itemData: display
property var tableView: table_view
property var tableModel: table_model
property var position: item_table.position
property int row: position.y
property int column: position.x
anchors.fill: parent
sourceComponent: {
if(itemData instanceof Component){
return itemData
}
return com_text
}
}
}
}
Loader{
id:item_loader
z:2
property var display
property int column
property int row
property var tableView: control
sourceComponent: undefined
onDisplayChanged: {
var obj = table_model.getRow(row)
obj[columnSource[column].dataIndex] = display
table_model.setRow(row,obj)
}
}
}
Component{
id:com_handle
Item {}
}
TableView {
id: header_horizontal
model: TableModel{
id:header_model
rows: d.header_rows
}
syncDirection: Qt.Horizontal
anchors.left: scroll_table.left
anchors.top: parent.top
implicitWidth: syncView ? syncView.width : 0
implicitHeight: Math.max(1, contentHeight)
syncView: table_view
boundsBehavior: Flickable.StopAtBounds
clip: true
delegate: Rectangle {
id:column_item_control
readonly property real cellPadding: 8
property bool canceled: false
readonly property var obj : columnSource[column]
implicitWidth: column_text.implicitWidth + (cellPadding * 2)
implicitHeight: Math.max(36, column_text.implicitHeight + (cellPadding * 2))
color:{
d.selectionFlag
if(column_item_control_mouse.pressed){
return control.pressedButtonColor
}
return column_item_control_mouse.containsMouse&&!canceled ? control.hoverButtonColor : FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(247/255,247/255,247/255,1)
}
border.color: FluTheme.dark ? "#252525" : "#e4e4e4"
FluText {
id: column_text
text: model.display
width: parent.width
height: parent.height
font.bold:{
d.selectionFlag
return true
}
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
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()
d.selectionFlag = !d.selectionFlag
}
}
MouseArea{
property point clickPos: "0,0"
height: parent.height
width: 6
anchors.right: parent.right
acceptedButtons: Qt.LeftButton
hoverEnabled: true
visible: !(obj.width === obj.minimumWidth && obj.width === obj.maximumWidth && obj.width)
cursorShape: Qt.SplitHCursor
onPressed :
(mouse)=>{
header_horizontal.interactive = false
FluTools.setOverrideCursor(Qt.SplitHCursor)
clickPos = Qt.point(mouse.x, mouse.y)
}
onReleased:{
header_horizontal.interactive = true
FluTools.restoreOverrideCursor()
}
onCanceled: {
header_horizontal.interactive = true
FluTools.restoreOverrideCursor()
}
onPositionChanged:
(mouse)=>{
if(!pressed){
return
}
var delta = Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y)
var minimumWidth = obj.minimumWidth
var maximumWidth = obj.maximumWidth
var w = obj.width
if(!w){
w = d.defaultItemWidth
}
if(!minimumWidth){
minimumWidth = d.defaultItemWidth
}
if(!maximumWidth){
maximumWidth = 65535
}
obj.width = Math.min(Math.max(minimumWidth, w + delta.x),maximumWidth)
table_view.forceLayout()
}
}
}
}
TableView {
id: header_vertical
boundsBehavior: Flickable.StopAtBounds
anchors.top: scroll_table.top
anchors.left: parent.left
implicitWidth: Math.max(1, contentWidth)
implicitHeight: syncView ? syncView.height : 0
syncDirection: Qt.Vertical
syncView: table_view
clip: true
model: TableModel{
TableModelColumn {}
rows: {
if(dataSource)
return dataSource
return []
}
}
delegate: Rectangle{
id:item_control
readonly property real cellPadding: 8
property bool canceled: false
implicitWidth: Math.max(30, row_text.implicitWidth + (cellPadding * 2))
implicitHeight: row_text.implicitHeight + (cellPadding * 2)
color: {
d.selectionFlag
if(item_control_mouse.pressed){
return control.pressedButtonColor
}
return item_control_mouse.containsMouse&&!canceled ? control.hoverButtonColor : FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(247/255,247/255,247/255,1)
}
border.color: FluTheme.dark ? "#252525" : "#e4e4e4"
FluText{
id:row_text
anchors.centerIn: parent
text: row + 1
font.bold:{
d.selectionFlag
return true
}
}
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()
d.selectionFlag = !d.selectionFlag
}
}
MouseArea{
property point clickPos: "0,0"
height: 6
width: parent.width
anchors.bottom: parent.bottom
acceptedButtons: Qt.LeftButton
cursorShape: Qt.SplitVCursor
visible: {
var obj = table_model.getRow(row)
return !(obj.height === obj.minimumHeight && obj.height === obj.maximumHeight && obj.height)
}
onPressed :
(mouse)=>{
header_vertical.interactive = false
FluTools.setOverrideCursor(Qt.SplitVCursor)
clickPos = Qt.point(mouse.x, mouse.y)
}
onReleased:{
header_vertical.interactive = true
FluTools.restoreOverrideCursor()
}
onCanceled: {
header_vertical.interactive = true
FluTools.restoreOverrideCursor()
}
onPositionChanged:
(mouse)=>{
if(!pressed){
return
}
var obj = table_model.getRow(row)
var delta = Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y)
var minimumHeight = obj.minimumHeight
var maximumHeight = obj.maximumHeight
var h = obj.height
if(!h){
h = d.defaultItemHeight
}
if(!minimumHeight){
minimumHeight = d.defaultItemHeight
}
if(!maximumHeight){
maximumHeight = 65535
}
obj.height = Math.min(Math.max(minimumHeight, h + delta.y),maximumHeight)
table_model.setRow(row,obj)
table_view.forceLayout()
}
}
}
}
function closeEditor(){
item_loader.sourceComponent = null
}
function resetPosition(){
scroll_bar_h.position = 0
scroll_bar_v.position = 0
}
}

View File

@ -0,0 +1,11 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
Text {
property color textColor: FluTheme.dark ? FluColors.White : FluColors.Grey220
id:text
color: textColor
renderType: FluTheme.nativeText ? Text.NativeRendering : Text.QtRendering
font: FluTextStyle.Body
}

View File

@ -0,0 +1,101 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
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)
property int iconRightMargin: icon_end.visible ? 25 : 5
property bool cleanEnabled: true
id:control
padding: 8
leftPadding: padding+2
enabled: !disabled
color: {
if(!enabled){
return disableColor
}
return normalColor
}
font:FluTextStyle.Body
renderType: FluTheme.nativeText ? Text.NativeRendering : Text.QtRendering
selectionColor:FluTools.colorAlpha(FluTheme.primaryColor.lightest,0.6)
selectedTextColor: color
placeholderTextColor: {
if(!enabled){
return placeholderDisableColor
}
if(focus){
return placeholderFocusColor
}
return placeholderNormalColor
}
selectByMouse: true
rightPadding: icon_end.visible ? 50 : 30
background: FluTextBoxBackground{
inputItem: control
implicitWidth: 240
FluIcon{
id:icon_end
iconSource: control.iconSource
iconSize: 15
opacity: 0.5
visible: control.iconSource != 0
anchors{
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: 5
}
}
}
Keys.onEnterPressed: (event)=> 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: control.echoMode !== TextInput.Password && menu.popup()
}
FluIconButton{
iconSource:FluentIcons.ChromeClose
iconSize: 10
width: 20
height: 20
verticalPadding: 0
horizontalPadding: 0
visible: {
if(control.cleanEnabled === false){
return false
}
if(control.readOnly)
return false
return control.text !== ""
}
anchors{
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: control.iconRightMargin
}
contentDescription:"清空"
onClicked:{
control.text = ""
}
}
FluTextBoxMenu{
id:menu
inputItem: control
}
}

View File

@ -0,0 +1,57 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15
import FluentUI 1.0
Rectangle{
property Item inputItem
id:content
radius: 4
layer.enabled: true
color: {
if(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.activeFocus){
return FluTheme.dark ? Qt.rgba(36/255,36/255,36/255,1) : Qt.rgba(1,1,1,1)
}
if(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)
}
layer.effect:OpacityMask {
maskSource: Rectangle {
width: content.width
height: content.height
radius: 4
}
}
border.width: 1
border.color: {
if(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.activeFocus ? 3 : 1
anchors.bottom: parent.bottom
visible: !inputItem.disabled
color: {
if(FluTheme.dark){
inputItem.activeFocus ? FluTheme.primaryColor.lighter : Qt.rgba(166/255,166/255,166/255,1)
}else{
return inputItem.activeFocus ? FluTheme.primaryColor.dark : Qt.rgba(183/255,183/255,183/255,1)
}
}
Behavior on height{
enabled: FluTheme.enableAnimation
NumberAnimation{
duration: 83
easing.type: Easing.OutCubic
}
}
}
}

View File

@ -0,0 +1,51 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
FluMenu{
property string cutText : "剪切"
property string copyText : "复制"
property string pasteText : "粘贴"
property string selectAllText : "全选"
property var inputItem
id:menu
enableAnimation: false
width: 120
onVisibleChanged: {
inputItem.forceActiveFocus()
}
Connections{
target: inputItem
function onTextChanged() {
menu.close()
}
}
FluMenuItem{
text: cutText
visible: inputItem.selectedText !== "" && !inputItem.readOnly
onClicked: {
inputItem.cut()
}
}
FluMenuItem{
text: copyText
visible: inputItem.selectedText !== ""
onClicked: {
inputItem.copy()
}
}
FluMenuItem{
text: pasteText
visible: inputItem.canPaste
onClicked: {
inputItem.paste()
}
}
FluMenuItem{
text: selectAllText
visible: inputItem.text !== ""
onClicked: {
inputItem.selectAll()
}
}
}

View File

@ -0,0 +1,64 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
Button {
property bool disabled: false
property string contentDescription: ""
property color normalColor: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
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.dark ? Qt.rgba(1,1,1,0.03) : Qt.rgba(0,0,0,0.03)
property color backgroundPressedColor: FluTheme.dark ? Qt.rgba(1,1,1,0.06) : Qt.rgba(0,0,0,0.06)
property color backgroundNormalColor: FluTheme.dark ? Qt.rgba(0,0,0,0) : Qt.rgba(0,0,0,0)
property color backgroundDisableColor: FluTheme.dark ? Qt.rgba(0,0,0,0) : Qt.rgba(0,0,0,0)
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
}
}

View File

@ -0,0 +1,400 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.Window 2.15
import FluentUI 1.0
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
id:control
color: {
if(mouse_area.containsMouse){
return hoverColor
}
return normalColor
}
height: 30
width: 300
radius: 4
border.width: 1
border.color: dividerColor
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:"时"
}
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:"分"
}
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:"AM/PM"
}
Menu{
id:popup
width: container.width
height: container.height
modal: true
dim:false
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){
if(FluTheme.dark){
return item_mouse.containsMouse ? Qt.darker(FluTheme.primaryColor.lighter,1.1) : FluTheme.primaryColor.lighter
}else{
return item_mouse.containsMouse ? Qt.lighter(FluTheme.primaryColor.dark,1.1): FluTheme.primaryColor.dark
}
}
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: Loader{
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: Loader{
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: ["上午","下午"]
clip: true
visible: isH
preferredHighlightBegin: 0
preferredHighlightEnd: 0
highlightMoveDuration: 0
ScrollBar.vertical: FluScrollBar {}
Layout.alignment: Qt.AlignVCenter
boundsBehavior:Flickable.StopAtBounds
delegate: Loader{
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: "取消"
onClicked: {
popup.close()
}
}
FluFilledButton{
anchors{
right: parent.right
left: divider.right
rightMargin: 20
leftMargin: 10
verticalCenter: parent.verticalCenter
}
text: "确定"
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 === "上午") ? 0 : 12;
} else {
hours24 = (period === "上午") ? hours24 : hours24 + 12;
}
}
date.setHours(hours24);
date.setMinutes(parseInt(minutes));
date.setSeconds(0);
current = date
}
}
}
}
}
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 = "下午"
hour = hour-12
}else{
ampm = "上午"
}
}else{
hour = now.getHours();
}
hour = text_hour.text === "时"? hour.toString().padStart(2, '0') : text_hour.text
var minute = text_minute.text === "分"? now.getMinutes().toString().padStart(2, '0') : text_minute.text
ampm = text_ampm.text === "AM/PM"?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;
}
}

View File

@ -0,0 +1,294 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
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: {
for(var i=0;i<model.count;i++){
var lable = model.get(i).lable
if(lable !== undefined && undefined !== ""){
return true
}
}
return false
}
property string stateName : {
if(hasLable){
return "Center"
}
if(isRight){
return "Right"
}
if(isAlternate){
return "Center"
}
return "Left"
}
}
Rectangle{
id:rect_line
color: control.lineColor
height: parent.height
width: 2
state: d.stateName
states: [
State {
name: "Left"
AnchorChanges {
target: rect_line
anchors.left: control.left
}
PropertyChanges {
target: rect_line
anchors.leftMargin: 7
}
},
State {
name: "Right"
AnchorChanges {
target: rect_line
anchors.right: control.right
}
PropertyChanges {
target: rect_line
anchors.rightMargin: 7
}
},
State {
name: "Center"
AnchorChanges {
target: rect_line
anchors.horizontalCenter: control.horizontalCenter
}
}
]
}
Component{
id:com_dot
Rectangle{
width: 16
height: 16
radius: 8
border.width: 4
color:FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
border.color: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
}
}
Component{
id:com_lable
FluText{
wrapMode: Text.WrapAnywhere
horizontalAlignment: isRight ? Qt.AlignRight : Qt.AlignLeft
text: modelData.lable
color: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
}
}
Component{
id:com_text
FluText{
wrapMode: Text.WrapAnywhere
horizontalAlignment: isRight ? Qt.AlignRight : Qt.AlignLeft
text: modelData.text
textFormat: Text.RichText
}
}
Column{
id:layout_column
spacing: 30
width: control.width
Repeater{
id:repeater
Item{
id:item_layout
width: layout_column.width
height: loader_text.height
Loader{
id:item_loader
state: d.stateName
states: [
State {
name: "Left"
AnchorChanges {
target: item_loader
anchors.left: item_layout.left
}
},
State {
name: "Right"
AnchorChanges {
target: item_loader
anchors.right: item_layout.right
}
},
State {
name: "Center"
AnchorChanges {
target: item_loader
anchors.horizontalCenter: item_layout.horizontalCenter
}
}
]
sourceComponent: {
if(model.dot)
return model.dot()
return com_dot
}
}
Loader{
property var modelData: control.model.get(index)
property bool isRight: state === "Right"
id:loader_lable
sourceComponent: {
if(!modelData){
return undefined
}
var lableDelegate = model.lableDelegate
if(lableDelegate instanceof Function && lableDelegate() instanceof Component){
return lableDelegate()
}
return com_lable
}
state: {
if(d.isRight){
return "Left"
}
if(d.isAlternate){
if(index%2===0){
return "Right"
}else{
return "Left"
}
}
return "Right"
}
states: [
State {
name: "Left"
AnchorChanges {
target: loader_lable
anchors.left: item_loader.right
anchors.right: item_layout.right
}
PropertyChanges {
target: loader_lable
anchors.leftMargin: 14
anchors.rightMargin: 14
}
},
State {
name: "Right"
AnchorChanges {
target: loader_lable
anchors.right: item_loader.left
anchors.left: item_layout.left
}
PropertyChanges {
target: loader_lable
anchors.leftMargin: 14
anchors.rightMargin: 14
}
},
State {
name: "Center"
AnchorChanges {
target: loader_lable
anchors.right: item_loader.left
anchors.left: item_layout.left
}
PropertyChanges {
target: loader_lable
anchors.leftMargin: 14
anchors.rightMargin: 14
}
}
]
}
Loader{
id:loader_text
property var modelData: control.model.get(index)
property bool isRight: state === "Right"
state: {
if(d.isRight){
return "Right"
}
if(d.isAlternate){
if(index%2===0){
return "Left"
}else{
return "Right"
}
}
return "Left"
}
sourceComponent: {
if(!modelData){
return undefined
}
var textDelegate = model.textDelegate
if(textDelegate instanceof Function && textDelegate() instanceof Component){
return textDelegate()
}
return com_text
}
states: [
State {
name: "Left"
AnchorChanges {
target: loader_text
anchors.left: item_loader.right
anchors.right: item_layout.right
}
PropertyChanges {
target: loader_text
anchors.leftMargin: 14
anchors.rightMargin: 14
}
},
State {
name: "Right"
AnchorChanges {
target: loader_text
anchors.right: item_loader.left
anchors.left: item_layout.left
}
PropertyChanges {
target: loader_text
anchors.leftMargin: 14
anchors.rightMargin: 14
}
},
State {
name: "Center"
AnchorChanges {
target: loader_text
anchors.right: item_loader.left
anchors.left: item_layout.left
}
PropertyChanges {
target: loader_text
anchors.leftMargin: 14
anchors.rightMargin: 14
}
}
]
}
}
}
}
}

View File

@ -0,0 +1,108 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
Button {
property bool disabled: false
property string contentDescription: ""
property color normalColor: {
if(checked){
return FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
}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(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(251/255,251/255,251/255,1)
}
}
property color disableColor: {
if(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(252/255,252/255,252/255,1)
}
}
property var clickListener : function(){
checked = !checked
}
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
horizontalPadding:12
onClicked: clickListener()
onCheckableChanged: {
if(checkable){
checkable = false
}
}
background: Rectangle{
implicitWidth: 28
implicitHeight: 28
radius: 4
border.color: FluTheme.dark ? "#505050" : "#DFDFDF"
border.width: checked ? 0 : 1
FluFocusRectangle{
visible: control.activeFocus
radius:8
}
color:{
if(!enabled){
return disableColor
}
if(checked){
if(pressed){
return pressedColor
}
}
return hovered ? hoverColor :normalColor
}
}
contentItem: FluText {
text: control.text
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: {
if(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(!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(!checked){
if(pressed){
return Qt.rgba(96/255,96/255,96/255,1)
}
}
return Qt.rgba(0,0,0,1)
}
}
}
}
}

View File

@ -0,0 +1,111 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import FluentUI 1.0
Button {
property bool disabled: false
property string contentDescription: ""
property color disableColor: FluTheme.dark ? Qt.rgba(82/255,82/255,82/255,1) : Qt.rgba(233/255,233/255,233/255,1)
property color checkColor: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
property color hoverColor: FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(240/255,240/255,240/255,1)
property color normalColor: FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(253/255,253/255,253/255,1)
property color borderNormalColor: FluTheme.dark ? Qt.rgba(161/255,161/255,161/255,1) : Qt.rgba(141/255,141/255,141/255,1)
property color borderCheckColor: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
property color borderDisableColor: FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(200/255,200/255,200/255,1)
property color dotNormalColor: FluTheme.dark ? Qt.rgba(208/255,208/255,208/255,1) : Qt.rgba(93/255,93/255,93/255,1)
property color dotCheckColor: FluTheme.dark ? Qt.rgba(0/255,0/255,0/255,1) : Qt.rgba(255/255,255/255,255/255,1)
property color dotDisableColor: FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(150/255,150/255,150/255,1)
property real textSpacing: 6
property bool textRight: true
property alias textColor: btn_text.textColor
property var clickListener : function(){
checked = !checked
}
id: control
Accessible.role: Accessible.Button
Accessible.name: control.text
Accessible.description: contentDescription
Accessible.onPressAction: control.clicked()
height: 20
enabled: !disabled
implicitHeight: height
focusPolicy:Qt.TabFocus
onClicked: clickListener()
onCheckableChanged: {
if(checkable){
checkable = false
}
}
contentItem: Item{}
background : RowLayout{
spacing: control.textSpacing
layoutDirection:control.textRight ? Qt.LeftToRight : Qt.RightToLeft
Rectangle {
id:control_backgound
width: 40
height: control.height
radius: height / 2
FluFocusRectangle{
visible: control.activeFocus
radius: 20
}
color: {
if(!enabled){
return disableColor
}
if(checked){
return checkColor
}
if(hovered){
return hoverColor
}
return normalColor
}
border.width: 1
border.color: {
if(!enabled){
return borderDisableColor
}
if(checked){
return borderCheckColor
}
return borderNormalColor
}
Rectangle {
width: 20
x:checked ? control_backgound.width-width : 0
height: 20
radius: 10
scale: hovered&enabled ? 7/10 : 6/10
color: {
if(!enabled){
return dotDisableColor
}
if(checked){
return dotCheckColor
}
return dotNormalColor
}
Behavior on x {
enabled: FluTheme.enableAnimation
NumberAnimation {
easing.type: Easing.OutCubic
}
}
Behavior on scale {
enabled: FluTheme.enableAnimation
NumberAnimation {
easing.type: Easing.OutCubic
}
}
}
}
FluText{
id:btn_text
text: control.text
Layout.alignment: Qt.AlignVCenter
visible: text !== ""
}
}
}

View File

@ -0,0 +1,29 @@
import QtQuick 2.15
import QtQuick.Templates 2.15 as T
import FluentUI 1.0
T.ToolTip {
id: control
x: parent ? (parent.width - implicitWidth) / 2 : 0
y: -implicitHeight - 3
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
contentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
contentHeight + topPadding + bottomPadding)
margins: 6
padding: 6
font: FluTextStyle.Body
closePolicy: T.Popup.CloseOnEscape | T.Popup.CloseOnPressOutsideParent | T.Popup.CloseOnReleaseOutsideParent
contentItem: FluText {
text: control.text
font: control.font
wrapMode: Text.Wrap
}
background: Rectangle {
color: FluTheme.dark ? Qt.rgba(50/255,49/255,48/255,1) : Qt.rgba(1,1,1,1)
radius: 3
FluShadow{
radius: 3
}
}
}

View File

@ -0,0 +1,201 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Shapes 1.15
import QtQuick.Window 2.15
import FluentUI 1.0
Popup{
property var steps : []
property int targetMargins: 5
property Component nextButton: com_next_button
property Component prevButton: com_prev_button
property int index : 0
id:control
padding: 0
anchors.centerIn: Overlay.overlay
width: d.windowWidth
height: d.windowHeight
background: Item{}
contentItem: Item{}
onVisibleChanged: {
if(visible){
control.index = 0
}
}
onIndexChanged: {
canvas.requestPaint()
}
Component{
id:com_next_button
FluFilledButton{
text: isEnd ? "结束导览" :"下一步"
onClicked: {
if(isEnd){
control.close()
}else{
control.index = control.index + 1
}
}
}
}
Component{
id:com_prev_button
FluButton{
text: "上一步"
onClicked: {
control.index = control.index - 1
}
}
}
Item{
id:d
property var window: Window.window
property int windowWidth: {
if(d.window)
return d.window.width
return 0
}
property int windowHeight: {
if(d.window)
return d.window.height
return 0
}
property var pos: Qt.point(0,0)
property var step : steps[index]
property var target : step.target()
}
Connections{
target: d.window
function onWidthChanged(){
canvas.requestPaint()
timer_delay.restart()
}
function onHeightChanged(){
canvas.requestPaint()
timer_delay.restart()
}
}
Timer{
id:timer_delay
interval: 200
onTriggered: {
canvas.requestPaint()
}
}
Canvas{
id:canvas
anchors.fill: parent
onPaint: {
d.pos = d.target.mapToGlobal(0,0)
d.pos = Qt.point(d.pos.x-d.window.x,d.pos.y-d.window.y)
var ctx = canvas.getContext("2d")
ctx.clearRect(0, 0, canvasSize.width, canvasSize.height)
ctx.save()
ctx.fillStyle = "#88000000"
ctx.fillRect(0, 0, canvasSize.width, canvasSize.height)
ctx.globalCompositeOperation = 'destination-out'
ctx.fillStyle = 'black'
var rect = Qt.rect(d.pos.x-control.targetMargins,d.pos.y-control.targetMargins, d.target.width+control.targetMargins*2, d.target.height+control.targetMargins*2)
ctx.fillRect(rect.x,rect.y,rect.width,rect.height)
ctx.restore()
}
//Todo
function drawRoundedRect(rect, r, ctx) {
var ptA = Qt.point(rect.x + r, rect.y)
var ptB = Qt.point(rect.x + rect.width, rect.y)
var ptC = Qt.point(rect.x + rect.width, rect.y + rect.height)
var ptD = Qt.point(rect.x, rect.y + rect.height)
var ptE = Qt.point(rect.x, rect.y)
ctx.beginPath()
ctx.moveTo(ptA.x, ptA.y)
ctx.arcTo(ptB.x, ptB.y, ptC.x, ptC.y, r)
ctx.arcTo(ptC.x, ptC.y, ptD.x, ptD.y, r)
ctx.arcTo(ptD.x, ptD.y, ptE.x, ptE.y, r)
ctx.arcTo(ptE.x, ptE.y, ptA.x, ptA.y, r)
ctx.fill()
ctx.closePath()
}
}
FluArea{
id:layout_panne
radius: 5
width: 500
height: 88 + text_desc.height
color: FluTheme.dark ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1)
x: Math.min(Math.max(0,d.pos.x+d.target.width/2-width/2),d.windowWidth-width)
y: d.pos.y+d.target.height+control.targetMargins + 15
border.width: 0
FluText{
text: d.step.title
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: d.step.description
anchors{
top: parent.top
left: parent.left
right: parent.right
rightMargin: 15
topMargin: 42
leftMargin: 15
}
}
Loader{
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
}
}
Loader{
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: FluentIcons.FlickDown
color: layout_panne.color
x: d.pos.x+d.target.width/2-10
y: d.pos.y+d.target.height
}
}

View File

@ -0,0 +1,292 @@
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import FluentUI 1.0
Item {
property int selectionMode: FluTreeViewType.None
property var currentElement
property var currentParentElement
property var rootModel: tree_model.get(0).items
signal itemClicked(var item)
id:root
ListModel{
id:tree_model
ListElement{
text: "根节点"
expanded:true
items:[]
key:"123456"
multipSelected:false
multipIndex:0
multipParentKey:""
}
}
Component{
id: delegate_root
Column{
width: calculateWidth()
property var itemModel: model
Repeater{
id: repeater_first_level
model: items
delegate: delegate_items
}
function calculateWidth(){
var w = 0;
for(var i = 0; i < repeater_first_level.count; i++) {
var child = repeater_first_level.itemAt(i)
if(w < child.width_hint){
w = child.width_hint;
}
}
return w;
}
}
}
Component{
id:delegate_items
Column{
id:item_layout
property real level: (mapToItem(list_root,0,0).x+list_root.contentX)/0.001
property var text: model.text??"Item"
property bool hasChild : (model.items !== undefined) && (model.items.count !== 0)
property var items: model.items??[]
property var expanded: model.expanded??true
property int width_hint: calculateWidth()
property bool singleSelected: currentElement === model
property var itemModel: model
function calculateWidth(){
var w = Math.max(list_root.width, item_layout_row.implicitWidth + 10);
if(expanded){
for(var i = 0; i < repeater_items.count; i++) {
var child = repeater_items.itemAt(i)
if(w < child.width_hint){
w = child.width_hint;
}
}
}
return w;
}
Item{
id:item_layout_rect
width: list_root.contentWidth
height: item_layout_row.implicitHeight
Rectangle{
anchors.fill: parent
anchors.margins: 2
color:{
if(FluTheme.dark){
if(item_layout.singleSelected && selectionMode === FluTreeViewType.Single){
return Qt.rgba(62/255,62/255,62/255,1)
}
return (item_layout_mouse.containsMouse || item_layout_expanded.hovered || item_layout_checkbox.hovered)?Qt.rgba(62/255,62/255,62/255,1):Qt.rgba(0,0,0,0)
}else{
if(item_layout.singleSelected && selectionMode === FluTreeViewType.Single){
return Qt.rgba(0,0,0,0.06)
}
return (item_layout_mouse.containsMouse || item_layout_expanded.hovered || item_layout_checkbox.hovered)?Qt.rgba(0,0,0,0.03):Qt.rgba(0,0,0,0)
}
}
Rectangle{
width: 3
color:FluTheme.primaryColor.dark
visible: item_layout.singleSelected && (selectionMode === FluTreeViewType.Single)
radius: 3
height: 20
anchors{
left: parent.left
verticalCenter: parent.verticalCenter
}
}
MouseArea{
id:item_layout_mouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
item_layout_rect.onClickItem()
}
}
}
function onClickItem(){
if(selectionMode === FluTreeViewType.None){
itemClicked(model)
}
if(selectionMode === FluTreeViewType.Single){
currentElement = model
if(item_layout.parent.parent.parent.itemModel){
currentParentElement = item_layout.parent.parent.parent.itemModel
}else{
if(item_layout.parent.itemModel){
currentParentElement = item_layout.parent.itemModel
}
}
itemClicked(model)
}
if(selectionMode === FluTreeViewType.Multiple){
}
}
RowLayout{
id:item_layout_row
anchors.verticalCenter: item_layout_rect.verticalCenter
Item{
width: 15*level
Layout.alignment: Qt.AlignVCenter
}
FluCheckBox{
id:item_layout_checkbox
text:""
checked: itemModel.multipSelected
visible: selectionMode === FluTreeViewType.Multiple
Layout.leftMargin: 5
function refreshCheckBox(){
const stack = [tree_model.get(0)];
const result = [];
while (stack.length > 0) {
const curr = stack.pop();
result.unshift(curr);
if (curr.items) {
for(var i=0 ; i<curr.items.count ; i++){
curr.items.setProperty(i,"multipIndex",i)
curr.items.setProperty(i,"multipParentKey",curr.key)
stack.push(curr.items.get(i));
}
}
}
for(var j=0 ; j<result.length-1 ; j++){
var item = result[j]
let obj = result.find(function(o) {
return o.key === item.multipParentKey;
});
if((item.items !== undefined) && (item.items.count !== 0)){
var items = item.items
for(var k=0 ; k<items.count ; k++){
if(items.get(k).multipSelected === false){
obj.items.setProperty(item.multipIndex,"multipSelected",false)
break
}
obj.items.setProperty(item.multipIndex,"multipSelected",true)
}
}
}
}
clickListener:function(){
if(hasChild){
const stack = [itemModel];
while (stack.length > 0) {
const curr = stack.pop();
if (curr.items) {
for(var i=0 ; i<curr.items.count ; i++){
curr.items.setProperty(i,"multipSelected",!itemModel.multipSelected)
stack.push(curr.items.get(i));
}
}
}
refreshCheckBox()
}else{
itemModel.multipSelected = !itemModel.multipSelected
refreshCheckBox()
}
}
}
FluIconButton{
id:item_layout_expanded
color:"#00000000"
opacity: item_layout.hasChild
onClicked: {
if(!item_layout.hasChild){
item_layout_rect.onClickItem()
return
}
model.expanded = !model.expanded
}
contentItem: FluIcon{
rotation: item_layout.expanded?0:-90
iconSource:FluentIcons.ChevronDown
iconSize: 15
Behavior on rotation {
enabled: FluTheme.enableAnimation
NumberAnimation{
duration: 167
easing.type: Easing.OutCubic
}
}
}
}
FluText {
text: item_layout.text
Layout.alignment: Qt.AlignVCenter
topPadding: 7
bottomPadding: 7
}
}
}
Item{
id:item_sub
visible: {
if(!hasChild){
return false
}
return item_layout.expanded??false
}
width: item_sub_layout.implicitWidth
height: item_sub_layout.implicitHeight
x:0.001
Column{
id: item_sub_layout
Repeater{
id:repeater_items
model: item_layout.items
delegate: delegate_items
}
}
}
}
}
ListView {
id: list_root
anchors.fill: parent
delegate: delegate_root
contentWidth: contentItem.childrenRect.width
model: tree_model
flickableDirection: Flickable.HorizontalAndVerticalFlick
clip: true
boundsBehavior: ListView.StopAtBounds
ScrollBar.vertical: FluScrollBar {}
ScrollBar.horizontal: FluScrollBar { }
}
function updateData(items){
rootModel.clear()
rootModel.append(items)
}
function signleData(){
return currentElement
}
function multipData(){
const stack = [tree_model.get(0)];
const result = [];
while (stack.length > 0) {
const curr = stack.pop();
if(curr.multipSelected){
result.push(curr)
}
for(var i=0 ; i<curr.items.count ; i++){
stack.push(curr.items.get(i));
}
}
return result
}
function createItem(text="",expanded=true,items=[],data={}){
return {text:text,expanded:expanded,items:items,key:uniqueRandom(),multipSelected:false,multipIndex:0,multipParentKey:"",data:data};
}
function uniqueRandom() {
var timestamp = Date.now();
var random = Math.floor(Math.random() * 1000000);
return timestamp.toString() + random.toString();
}
}

View File

@ -0,0 +1,138 @@
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import FluentUI 1.0
Window {
default property alias content: container.data
property bool closeDestory: true
property int launchMode: FluWindowType.Standard
property var argument:({})
property var background : com_background
property Component loadingItem: com_loading
property color backgroundColor: {
if(active){
return FluTheme.dark ? Qt.rgba(26/255,34/255,40/255,1) : Qt.rgba(243/255,243/255,243/255,1)
}
return FluTheme.dark ? Qt.rgba(32/255,32/255,32/255,1) : Qt.rgba(237/255,237/255,237/255,1)
}
property var _pageRegister
property string _route
property var closeFunc: function(event){
if(closeDestory){
deleteWindow()
}else{
visible = false
event.accepted = false
}
}
signal initArgument(var argument)
id:window
color:"transparent"
Component.onCompleted: {
helper.initWindow(window)
initArgument(argument)
}
Connections{
target: window
function onClosing(event){closeFunc(event)}
}
Component{
id:com_background
Rectangle{
color: window.backgroundColor
}
}
Loader{
anchors.fill: parent
sourceComponent: background
}
Item{
id:container
anchors.fill: parent
clip: true
}
Loader{
property string loadingText: "加载中..."
property bool cancel: false
id:loader_loading
anchors.fill: container
}
FluInfoBar{
id:infoBar
root: window
}
Component{
id:com_loading
Popup{
id:popup_loading
modal:true
focus: true
anchors.centerIn: Overlay.overlay
closePolicy: {
if(cancel){
return Popup.CloseOnEscape | Popup.CloseOnPressOutside
}
return Popup.NoAutoClose
}
Overlay.modal: Rectangle {
color: "#44000000"
}
onVisibleChanged: {
if(!visible){
loader_loading.sourceComponent = undefined
}
}
visible: true
background: Item{}
contentItem: Item{
ColumnLayout{
spacing: 8
anchors.centerIn: parent
FluProgressRing{
Layout.alignment: Qt.AlignHCenter
}
FluText{
text:loadingText
Layout.alignment: Qt.AlignHCenter
}
}
}
}
}
WindowHelper{
id:helper
}
function showLoading(text = "加载中...",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){
infoBar.showSuccess(text,duration,moremsg)
}
function showInfo(text,duration,moremsg){
infoBar.showInfo(text,duration,moremsg)
}
function showWarning(text,duration,moremsg){
infoBar.showWarning(text,duration,moremsg)
}
function showError(text,duration,moremsg){
infoBar.showError(text,duration,moremsg)
}
function registerForWindowResult(path){
return helper.createRegister(window,path)
}
function deleteWindow(){
FluApp.deleteWindow(window)
}
function onResult(data){
if(_pageRegister){
_pageRegister.onResult(data)
}
}
}

View File

Before

Width:  |  Height:  |  Size: 266 KiB

After

Width:  |  Height:  |  Size: 266 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,95 @@
module FluentUI
classname FluentUIPlugin
designersupported
typeinfo plugins.qmltypes
ColorPicker 1.0 Controls/ColorPicker/ColorPicker.qml
Checkerboard 1.0 Controls/ColorPicker/Content/Checkerboard.qml
ColorSlider 1.0 Controls/ColorPicker/Content/ColorSlider.qml
NumberBox 1.0 Controls/ColorPicker/Content/NumberBox.qml
PanelBorder 1.0 Controls/ColorPicker/Content/PanelBorder.qml
SBPicker 1.0 Controls/ColorPicker/Content/SBPicker.qml
FluAcrylic 1.0 Controls/FluAcrylic.qml
FluAppBar 1.0 Controls/FluAppBar.qml
FluArea 1.0 Controls/FluArea.qml
FluAutoSuggestBox 1.0 Controls/FluAutoSuggestBox.qml
FluBadge 1.0 Controls/FluBadge.qml
FluBreadcrumbBar 1.0 Controls/FluBreadcrumbBar.qml
FluButton 1.0 Controls/FluButton.qml
FluCalendarPicker 1.0 Controls/FluCalendarPicker.qml
FluCalendarView 1.0 Controls/FluCalendarView.qml
FluCarousel 1.0 Controls/FluCarousel.qml
FluChart 1.0 Controls/FluChart.qml
FluCheckBox 1.0 Controls/FluCheckBox.qml
FluColorPicker 1.0 Controls/FluColorPicker.qml
FluColorView 1.0 Controls/FluColorView.qml
FluComboBox 1.0 Controls/FluComboBox.qml
FluContentDialog 1.0 Controls/FluContentDialog.qml
FluContentPage 1.0 Controls/FluContentPage.qml
FluControl 1.0 Controls/FluControl.qml
FluCopyableText 1.0 Controls/FluCopyableText.qml
FluDatePicker 1.0 Controls/FluDatePicker.qml
FluDivider 1.0 Controls/FluDivider.qml
FluDropDownButton 1.0 Controls/FluDropDownButton.qml
FluExpander 1.0 Controls/FluExpander.qml
FluFilledButton 1.0 Controls/FluFilledButton.qml
FluFlipView 1.0 Controls/FluFlipView.qml
FluFocusRectangle 1.0 Controls/FluFocusRectangle.qml
FluIcon 1.0 Controls/FluIcon.qml
FluIconButton 1.0 Controls/FluIconButton.qml
FluImage 1.0 Controls/FluImage.qml
FluInfoBar 1.0 Controls/FluInfoBar.qml
FluItem 1.0 Controls/FluItem.qml
FluItemDelegate 1.0 Controls/FluItemDelegate.qml
FluMenu 1.0 Controls/FluMenu.qml
FluMenuBar 1.0 Controls/FluMenuBar.qml
FluMenuBarItem 1.0 Controls/FluMenuBarItem.qml
FluMenuItem 1.0 Controls/FluMenuItem.qml
FluMenuSeparator 1.0 Controls/FluMenuSeparator.qml
FluMultilineTextBox 1.0 Controls/FluMultilineTextBox.qml
FluNavigationView 1.0 Controls/FluNavigationView.qml
FluObject 1.0 Controls/FluObject.qml
FluPage 1.0 Controls/FluPage.qml
FluPagination 1.0 Controls/FluPagination.qml
FluPaneItem 1.0 Controls/FluPaneItem.qml
FluPaneItemEmpty 1.0 Controls/FluPaneItemEmpty.qml
FluPaneItemExpander 1.0 Controls/FluPaneItemExpander.qml
FluPaneItemHeader 1.0 Controls/FluPaneItemHeader.qml
FluPaneItemSeparator 1.0 Controls/FluPaneItemSeparator.qml
FluPasswordBox 1.0 Controls/FluPasswordBox.qml
FluPivot 1.0 Controls/FluPivot.qml
FluPivotItem 1.0 Controls/FluPivotItem.qml
FluPopup 1.0 Controls/FluPopup.qml
FluProgressBar 1.0 Controls/FluProgressBar.qml
FluProgressRing 1.0 Controls/FluProgressRing.qml
FluQRCode 1.0 Controls/FluQRCode.qml
FluRadioButton 1.0 Controls/FluRadioButton.qml
FluRadioButtons 1.0 Controls/FluRadioButtons.qml
FluRatingControl 1.0 Controls/FluRatingControl.qml
FluRectangle 1.0 Controls/FluRectangle.qml
FluRemoteLoader 1.0 Controls/FluRemoteLoader.qml
FluScreenshot 1.0 Controls/FluScreenshot.qml
FluScrollBar 1.0 Controls/FluScrollBar.qml
FluScrollIndicator 1.0 Controls/FluScrollIndicator.qml
FluScrollablePage 1.0 Controls/FluScrollablePage.qml
FluShadow 1.0 Controls/FluShadow.qml
FluSlider 1.0 Controls/FluSlider.qml
FluSpinBox 1.0 Controls/FluSpinBox.qml
FluStatusView 1.0 Controls/FluStatusView.qml
FluTabView 1.0 Controls/FluTabView.qml
FluTableModelColumn 1.0 Controls/FluTableModelColumn.qml
FluTableView 1.0 Controls/FluTableView.qml
FluText 1.0 Controls/FluText.qml
FluTextBox 1.0 Controls/FluTextBox.qml
FluTextBoxBackground 1.0 Controls/FluTextBoxBackground.qml
FluTextBoxMenu 1.0 Controls/FluTextBoxMenu.qml
FluTextButton 1.0 Controls/FluTextButton.qml
FluTimePicker 1.0 Controls/FluTimePicker.qml
FluTimeline 1.0 Controls/FluTimeline.qml
FluToggleButton 1.0 Controls/FluToggleButton.qml
FluToggleSwitch 1.0 Controls/FluToggleSwitch.qml
FluTooltip 1.0 Controls/FluTooltip.qml
FluTour 1.0 Controls/FluTour.qml
FluTreeView 1.0 Controls/FluTreeView.qml
FluWindow 1.0 Controls/FluWindow.qml
plugin fluentuiplugin

Some files were not shown because too many files have changed in this diff Show More